Kotlin基础学习笔记 (三)

二、Kotlin 基础程序结构
 1、Val和Var
    1.1、val=value,值类型 ,类似Java的final修饰,不可以重复赋值
    ps:类似于java的中的final关键字修饰的 ,所以一般val 修饰的词为Final类型值
val定义的是常量
 
    val FINAL_HELLO_CHINA = “hello china” //类型推导(Java中需要必须写明final和修饰关键字)
 
        在Java中含有final关键字修饰的变量,变量FINAL_HELLO_WORLD为编译期常量,在用到这个变量的时候,编译器一律把 “对这个变量的引用都会自动换成 它的值类型进行使用”,提高代码运行效率。
public String FINAL_HELLO_WORLD =“...“;
 
ex:
~运行时常量;val x =getX()
~编译期常量: const val x:值类型=值
    
   1.2、 var = variable 临时变量                                                                                                                                                                                                                                                                  
ex: 
    var x = “HelloWorld “ //定义变量
        x = “ hiWolrd “  //再次赋值
 
类型推导
    val String = “hello” //推导出String类型
    val int = 5 //Int类型
    var x = getString() + 5 //String 类型
 
 2、函数 Function
    什么是函数?
 以特定功能组织起来的代码块
    fun[函数名](参数列表):[返回值类型] { 函数体}
    fun[函数名](参数列表) =  表达式
ex:
    fun sayFuck( name: String ) : String{  println(“fuck,$name”)  }
    fun sayFuck( name: String ) = println(“fuck,$name")
 
什么是匿名函数?
fun(参数列表),必须赋值给一个变量否则无法识别,如上例2
 
值类型返回值要加参数类型 ,默认返回类型是Unit
fun main(args: Array<String>) {
    val arg1 = args[0].toInt()
    val arg2 = args[1].toInt()
    println("$arg1+$arg2= ${sum(arg1, arg2)}")
}
 
fun sum(arg1: Int, arg2: Int): Int {
    return arg1 + arg2
}
kotlin简便写法:
   fun sum(arg1: Int, arg2: Int) = arg1 + arg2
 
思考:方法一定有名字嘛?
    答:用一个变量接收一个方法就不需要有名字了:
            也就是一个变量的值是一个函数的返回值
        val int2 = fun(x:Int ) : Long {
            return x.toLong()
        }
注意事项
  • 方法要功能单一
  • 函数名顾名思义
  • 参数列表不要太复杂
 
3、类
 
ex:
public class JavaA {
    private int b = 0;
    public int getB() {
        System.out.println("some one tyies to get b");
        return b;
    }
    public void setB(int b) {
        System.out.println("some one tyies to set b");
        this.b = b;
    }
}
 
class A {
在kotlin方法中get()、set()是默认实现的版本,想要实现必须得复写get or set方法
    var b = 0;
        get() {
            println("...")
            return field   //field 这里指代b后面真正的值,只在get/set访问器中才能访问到
        }
        set(value){
         field = value ; //相当于java中的this .b=b;
        }
}
 
lateinit 延时初始化 and by lazy 懒加载
lateinit var c:String  
延时初始化,在需要的时候进行初始化,禁止初始化之前使用
 
lateinit val e:x  is false 
var 可以使用 lateinit 进行延时初始化,也只能使用在var上 ,
 
   但val的延时初始化需要用下面的操作:
 val e : X by lazy {
                … //适合于val的懒加载模式
            }
 
当var xx:String ?= null   //初始化为null并且可空类型
在使用的时候不可使用,因为没有向编译器保证该值不能为null
解决办法:
 println(cc ?. length)  or  println(cc !!. length)
 
类成员
属性:或者说成员变量,类范围内的变量
方法:或者说成员函数,类范围内的函数
 
构造方法 参数中 val / var 修饰的都是属性,没有就只是构造方法的参数
 
1、val 没有set()方法因为是不可变的final的
2、 属性的初始化尽量在构造方法中完成。
3、无法在构造方法中完成初始化,尝试降级为局部变量
4、var用lateinit延迟初始化,val用lazy
5、可空类型谨慎用null直接初始化 (!!or?)
 
4、修饰符
 kotlin 语言的修饰符 存放在kotlin源码工程的kotlin/grammar/src/modifiers.grm 文件中,完整定义在kotlin/compiler/frontend/src/org/jetbrains/kotlin/lexer/KtToken.java源码中
/**
## Modifiers
*/
 
modifiers
  : (modifier| annotations)*
  
typeModifiers
  : (suspendModifier| annotations)*
 
modifier
  : classModifier
  : accessModifier
  : varianceAnnotation
  : memberModifier
  : parameterModifier
  : typeParameterModifier
  : functionModifier
  : propertyModifier
 
classModifier 类修饰符
  : "abstract" 抽象类
  : "final" 不可被继承final类
  : "enum" 枚举类
  : "open" 可继承open类
  : "annotation" 注解类
  : "sealed" 密封类
  : "data" 数据类
  
memberModifier
  : "override" 重写函数
  : "open" 可被重写
  : "final" 不可被重写
  : "abstract" 抽象函数
  : "lateinit" 后期初始化
  
accessModifier 访问权限控制, 默认是public
  : "private"
  : "protected"
  : "public"
  : "internal"  整个模块内(模块(module)是指一起编译的一组 Kotlin 源代码文件: 例如,一个 IntelliJ IDEA 模块,一个 Maven 工程, 或 Gradle 工程,通过 Ant 任务的一次调用编译的一组文件等)可访问;
 
varianceAnnotation 泛型可变性
  : "in" 
  : "out"
 
parameterModifier
  : "noinline" 
  : "crossinline" 
  : "vararg" 变长参数
 
typeParameterModifier
  : "reified"
  
functionModifier
  : "tailrec" 尾递归
  : "operator"
  : "infix"
  : "inline"
  : "external"
  : suspendModifier
 
propertyModifier
  : "const" 
 
suspendModifier
  : "suspend"
 
5、关键字 keywords
    一些常见的关键字:
    KtKeywordToken PACKAGE_KEYWORD          = KtKeywordToken.keyword("package");
    KtKeywordToken AS_KEYWORD               = KtKeywordToken.keyword("as");
    KtKeywordToken TYPE_ALIAS_KEYWORD       = KtKeywordToken.keyword("typealias");
    KtKeywordToken CLASS_KEYWORD            = KtKeywordToken.keyword("class");
    KtKeywordToken THIS_KEYWORD             = KtKeywordToken.keyword("this");
    KtKeywordToken SUPER_KEYWORD            = KtKeywordToken.keyword("super");
    KtKeywordToken VAL_KEYWORD              = KtKeywordToken.keyword("val");
    KtKeywordToken VAR_KEYWORD              = KtKeywordToken.keyword("var");
    KtKeywordToken FUN_KEYWORD              = KtKeywordToken.keyword("fun");
    KtKeywordToken FOR_KEYWORD              = KtKeywordToken.keyword("for");
    KtKeywordToken NULL_KEYWORD             = KtKeywordToken.keyword("null");
    KtKeywordToken TRUE_KEYWORD             = KtKeywordToken.keyword("true");
    KtKeywordToken FALSE_KEYWORD            = KtKeywordToken.keyword("false");
    KtKeywordToken IS_KEYWORD               = KtKeywordToken.keyword("is");
    KtModifierKeywordToken IN_KEYWORD       = KtModifierKeywordToken.keywordModifier("in");
    KtKeywordToken THROW_KEYWORD            = KtKeywordToken.keyword("throw");
    KtKeywordToken RETURN_KEYWORD           = KtKeywordToken.keyword("return");
    KtKeywordToken BREAK_KEYWORD            = KtKeywordToken.keyword("break");
    KtKeywordToken CONTINUE_KEYWORD         = KtKeywordToken.keyword("continue");
    KtKeywordToken OBJECT_KEYWORD           = KtKeywordToken.keyword("object");
    KtKeywordToken IF_KEYWORD               = KtKeywordToken.keyword("if");
    KtKeywordToken TRY_KEYWORD              = KtKeywordToken.keyword("try");
    KtKeywordToken ELSE_KEYWORD             = KtKeywordToken.keyword("else");
    KtKeywordToken WHILE_KEYWORD            = KtKeywordToken.keyword("while");
    KtKeywordToken DO_KEYWORD               = KtKeywordToken.keyword("do");
    KtKeywordToken WHEN_KEYWORD             = KtKeywordToken.keyword("when");
    KtKeywordToken INTERFACE_KEYWORD        = KtKeywordToken.keyword("interface");
 
    // Reserved for future use:
    KtKeywordToken TYPEOF_KEYWORD           = KtKeywordToken.keyword("typeof");
        ...
    KtKeywordToken FILE_KEYWORD    = KtKeywordToken.softKeyword("file");
    KtKeywordToken FIELD_KEYWORD     = KtKeywordToken.softKeyword("field");
    KtKeywordToken PROPERTY_KEYWORD     = KtKeywordToken.softKeyword("property");
    KtKeywordToken RECEIVER_KEYWORD     = KtKeywordToken.softKeyword("receiver");
    KtKeywordToken PARAM_KEYWORD     = KtKeywordToken.softKeyword("param");
    KtKeywordToken SETPARAM_KEYWORD  = KtKeywordToken.softKeyword("setparam");
    KtKeywordToken DELEGATE_KEYWORD  = KtKeywordToken.softKeyword("delegate");
    KtKeywordToken IMPORT_KEYWORD    = KtKeywordToken.softKeyword("import");
    KtKeywordToken WHERE_KEYWORD     = KtKeywordToken.softKeyword("where");
    KtKeywordToken BY_KEYWORD        = KtKeywordToken.softKeyword("by");
    KtKeywordToken GET_KEYWORD       = KtKeywordToken.softKeyword("get");
    KtKeywordToken SET_KEYWORD       = KtKeywordToken.softKeyword("set");
    KtKeywordToken CONSTRUCTOR_KEYWORD = KtKeywordToken.softKeyword("constructor");
    KtKeywordToken INIT_KEYWORD        = KtKeywordToken.softKeyword("init");
 
    KtModifierKeywordToken ABSTRACT_KEYWORD  = KtModifierKeywordToken.softKeywordModifier("abstract");
    KtModifierKeywordToken ENUM_KEYWORD      = KtModifierKeywordToken.softKeywordModifier("enum");
    KtModifierKeywordToken OPEN_KEYWORD      = KtModifierKeywordToken.softKeywordModifier("open");
    KtModifierKeywordToken INNER_KEYWORD     = KtModifierKeywordToken.softKeywordModifier("inner");
    KtModifierKeywordToken OVERRIDE_KEYWORD  = KtModifierKeywordToken.softKeywordModifier("override");
    KtModifierKeywordToken PRIVATE_KEYWORD   = KtModifierKeywordToken.softKeywordModifier("private");
    KtModifierKeywordToken PUBLIC_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("public");
    KtModifierKeywordToken INTERNAL_KEYWORD  = KtModifierKeywordToken.softKeywordModifier("internal");
    KtModifierKeywordToken PROTECTED_KEYWORD = KtModifierKeywordToken.softKeywordModifier("protected");
    KtKeywordToken CATCH_KEYWORD     = KtKeywordToken.softKeyword("catch");
    KtModifierKeywordToken OUT_KEYWORD       = KtModifierKeywordToken.softKeywordModifier("out");
    KtModifierKeywordToken VARARG_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("vararg");
    KtModifierKeywordToken REIFIED_KEYWORD   = KtModifierKeywordToken.softKeywordModifier("reified");
    KtKeywordToken DYNAMIC_KEYWORD   = KtKeywordToken.softKeyword("dynamic");
    KtModifierKeywordToken COMPANION_KEYWORD = KtModifierKeywordToken.softKeywordModifier("companion");
    KtModifierKeywordToken SEALED_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("sealed");
    KtModifierKeywordToken DEFAULT_VISIBILITY_KEYWORD = PUBLIC_KEYWORD;
    KtKeywordToken FINALLY_KEYWORD   = KtKeywordToken.softKeyword("finally");
    KtModifierKeywordToken FINAL_KEYWORD     = KtModifierKeywordToken.softKeywordModifier("final");
    KtModifierKeywordToken LATEINIT_KEYWORD = KtModifierKeywordToken.softKeywordModifier("lateinit");
    KtModifierKeywordToken DATA_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("data");
    KtModifierKeywordToken INLINE_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("inline");
    KtModifierKeywordToken NOINLINE_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("noinline");
    KtModifierKeywordToken TAILREC_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("tailrec");
    KtModifierKeywordToken EXTERNAL_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("external");
    KtModifierKeywordToken ANNOTATION_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("annotation");
    KtModifierKeywordToken CROSSINLINE_KEYWORD    = KtModifierKeywordToken.softKeywordModifier("crossinline");
    KtModifierKeywordToken OPERATOR_KEYWORD = KtModifierKeywordToken.softKeywordModifier("operator");
    KtModifierKeywordToken INFIX_KEYWORD = KtModifierKeywordToken.softKeywordModifier("infix");
 
    KtModifierKeywordToken CONST_KEYWORD = KtModifierKeywordToken.softKeywordModifier("const");
    KtModifierKeywordToken SUSPEND_KEYWORD = KtModifierKeywordToken.softKeywordModifier("suspend");
    KtModifierKeywordToken HEADER_KEYWORD = KtModifierKeywordToken.softKeywordModifier("header");
    KtModifierKeywordToken IMPL_KEYWORD = KtModifierKeywordToken.softKeywordModifier("impl");
 
this 关键字
  • 持有当前对象的引用,使用this来引用变量或是成员函数,亦可以使用return this,来返回某个类的引用
  • 在类的成员中,this指向的是该类的当前对象
  • 在扩展函数或者带接收者的函数字面值中,this表示在点左侧传递的 接收者参数
  • 如果this没有限定符,它指的是最内层的包含它的作用域;如果引用其他作用域中的this,可以使用this@label标签
 
小示例:
class Outer {
    val oh = "Oh!"
    inner class Inner {
        fun m() {
            val outer = this@Outer
            val inner = this@Inner
            val pthis = this
            println("outer=" + outer)
            println("inner=" + inner)
            println("pthis=" + pthis)
            println(this@Outer.oh)
            val fun1 = hello@ fun String.() {
                val d1 = this // fun1 的接收者
                println("d1" + d1)
            }
            val fun2 = { s: String ->
                val d2 = this
                println("d2=" + d2)
            }
            "abc".fun1()
            fun2
        }
    }
}
 
6、基本运算(操作)符
  • 运算符方法重载的定义,任何类可以定义或是重载基本运算符
  • 通过运算符对应的具名函数来定义,要求方法名字必须一致对的上,fun前面加上operator 关键字
  • 参数的个数对的上,类型和返回类型不作要求
  • 不能像Scala一样定义任意运算符
 
6.1、操作符优先级
    级别由上到下 ,由高到低
        后缀 Postfix   :    “++”  , " - - “ ,”  .”  ,” ?.” ,” ?"
        前缀 Prefix    : "- "  ,"+ ", "++ ", " - - " , “!” ,”labelDefinition@"
        右手类型运算  RHS:” :" , " as " , "as?"
        乘除取余  Multiplicative :  "* “  , “ / “ , “%"
        加减     Additive :  “+” , “-"
        区间范围  Range   :  " ..”
        Infix 函数   :   给Int 定义扩展 infix fun Int .shl( x:Int ) :Int {…} —>调用 1 shl 2 ,等同于1.shl(2)
        Elvis 操作符 :“ ?:”
        命名检查符号 Named checks: “in" , "!in”, “is”, “!is"
        比较大小 Compariosn : “<“ , “>” ,”<=“ , “>="
        相等性判断 Equality  :  “==“  , “!=="
        与     Conjunction  :   “&&"
        或      DIsjunction  :    “||"
    最低 赋值 Assignment : “ = “ , “+= “ ,” -= “ , “*=“, ”/=“ , “%="       
 
6.2 、递增 or 递减  
   inc ()  / dec () 函数必须返回一个值,用于赋值给使用 ++  or  — 操作的变量
    a ++  :  a.inc() 返回值 a
    a - - : a.dec () 返回值 a
    ++ a : a.inc() 返回值 a+1
    —a : a.dec() 返回值是 a-1   
 
解析:
    编译器执行以下步骤来解析后缀表达式的操作符,例如:后缀形式a++ 
        1、确定a的类型 ,令其解析为T
        2、查找一个适用于类型为T的接收者的、带有operator修饰符的无参数函数inc()
        3、检查函数的返回类型是T的子类型
 
    表达式计算步骤:
        1、把a的初始值存储到临时存储a _ 中
        2、把a.inc ( ) 结果赋值给 a
        3、把a_ 作为表达式的结果返回
    对于前缀形式:
        ++a 和 —a解析步骤类似,返回值不同是取的新值来返回
            把 a.inc ()  结果赋值给a
            把 a的新值 a+1 作为表达式结果返回
 
6.3 、算术运算符
    a + b   : a.plus ( b )
    a - b    : a.minus ( b )
    a * b    : a.times ( b )
    a / b    : a.div ( b )
    a & b   : a.rem ( b ) a.mod ( b )
    a .. b    : a .rangTo ( b )
 
6.4 、in 操作符
    a in b   : b.contains ( a )
    a !in b  : !b.contains ( a )
 
6.5 、计算并赋值
    a += b  : a .plusAssign ( b )
    a  -= b : a .minusAssign ( b )
    a *=  b : a .timesAssign ( b )
    a /=  b : a .divAssign ( b )
    a %= b : a.modAssign ( b )
编译器操作 a += b : 生成a = a+b 的代码,a+b 的类型必须是a的子类
 
6.6、相等 与 不等操作符
  •  引用相等 "===" , " !==“  ————》两个引用指向同一对象————〉不能重载!!!
  •  结构相等 “ == “ , “ != “  ————> 使用 equals() 判断
a == b   :    a ?.equals ( b ) ?: ( b === null )
a != b   :   ! (  a ?.equals ( b ) ?: ( b === null ) )
在这里 == 操作符 特殊性:被翻译成一个复杂的表达式,用于筛选 null 值;
也就是说如果 a不是null,则调用 equals ( Any )函数并返回其值 ,否则 ( a === null ) 就计算 ( b === null )的值并返回
当与 null 显示 比较时候, a == null 会被自动转换为 a === null 
 
6.7 、 Elvis 操作符 ?:
    在kotlin 中, Elvis 操作符 特定是跟null 比较 ,也就是说用来做null安全检查
 
        y = x ?: 0 ——>二元
等价于==》
        val y = if ( x !== null ) x else 0  )——>kotlin 三元运算符
 
?:  这是一个二元运算符 ,如果第一个操作数为真,则返回第一个操作数,否则将计算并返回其第二个操作数
 
注意:
    用( ?: ) 可以把带有默认值的if/else 结构写的精简,不用检查null,也不用重复变量
 
6.8 、中缀操作符
    通过自定义infix函数来实现中缀操作符
示例:       
 
 data class  Person ( val name :String .val age :Int )
        infix fun Person.grow ( years:Int ) :Person{
                return Person ( name ,age +years )
    }
    test:
@RunWith(Junit4::Class )
    class InfixFunctionTest {
    @Test
    fun testInfixFuntion ( ){
        val person =  Person (“son” ,25)
        println ( person .grow (2 ) )
        println ( person  grow 2 )
}
结果:
        name = son, /n age = 27
 
小示例
 加法:
class Complex(var real: Double, var imaginary: Double) {
 
    //定义一个加法函数
    operator fun plus(other: Complex): Complex {
        return Complex(real +other.real, imaginary + other.imaginary)
    }
 
    operator fun plus(other: Int): Complex {
        return Complex(real + other, imaginary)
    }
    override fun toString(): String {
        return "$real+${imaginary} i"//实部+虚部
    }
   //取模方法
operator fun invoke() :Double{
    return Math.hypot(real, imaginary)
}
 
class Book {
    //中缀表达式 infix
    infix fun on(any: Any): Boolean {
        return false
    }
}
 
    fun main(args: Array<String>) {
        val c1 = Complex(3.0, 4.0)  //3+4i
        val c2 = Complex(2.0, 7.5)  //2+7.5i
        println(c1 + c2)  //5+11.5i—>toString() 需要用到
        println(c1 + 4)
      println(c1)    //(3的平方+4的平方) 再开方
 
//-name <Name>  //args contains name,如果包含的话就取后面的Name
if ("-name" in args) {    //in表示Array<out T>.contains(element: T): Boolean,
    
// 实际上返回的是indexOf >=0,如果indexOf有这个元素,返回的就是它的位置如果没有返回 -1
   
println(args[args.indexOf("-name") + 1])//返回-name在args里面的后面一个位置的值
}
if(Book() on Desk()) { … }//DSL里常见 中缀表达式替换.()方法
    }
}
 
 7、Lambda表达式 *****
本质:匿名函数(可以赋值和传递)
lambda表达式返回值 :表达式最后一行表达式的值
Lambda表达式(匿名函数) 
写法:{ [参数列表]->[函数体,最后一行时返回值] }
 
ex: 
    普通函数表达式:fun sum( arg1:Int, arg2:Int ) = arg1 + arg2
    lambda 表达式  :  val sum ={ arg1:Int ,arg2 :Int -> arg1+arg2 }
       参数列表:arg1:Int ,arg2 :Int 
        函数体,返回值最后一行:arg1+arg2
    -> xxx :lambda表达式返回值是表达式最后一行的值
 
val/var  xxx = {函数体} ps:arg1:Int,arg2:Int ->arg1+arg2 
arg1:Int 表示参数返回类型
arg1+arg2:函数返回值
->:用右箭头链接 
lambda表达式可以作为参数传递!
 
print(sum(1, 2))===  print(sum.invoke(1,3))是一个意思!—【invoke :运算符重载】
 
在kotlin中数组的遍历两种写法:
    for( i  in  args) { … }     or for ( i in array.indices) {...}                //在Program arguments里: 添加参数中间用空格隔开
   args.forEach(  {…lambda...}  )
 
注意:函数如果最后一个参数是lambda表达式,可以将括号内的内容移动到括号外面,若括号内无内容可直接删除。
forEach: Array的扩展方法 ,返回值是Unit类型,使用action调用传递的Array里面的元素作为参数
Array<out T> .forEach (action : (T) -> Unit ) :Unit {for (element in this ) action (element) }
action : (T):参数的类型
->Unit :lambda 表达式返回的类型
(action : (T) -> Unit )==lambda
 
args.forEach( {println(it)} )  === args.forEach(){ println(it) }====>  
注意:若参数的最后一个是lambda表达式,可以将( ) 移动到lambda表达式外面 
 
args.forEach(::println) //[若传入的函数和lambda表达式类型完全一样]
    将println函数作为参数传递 ,println的参数是Any,接收任何对象都可以
 
args.forEachForEach@{
 if(i=x)    return@ForEach
    println(it)
}
println(...)
终断lambda表达式的迭代。以上做法可以跳出lambda表达式内的执行继续执行lambda表达式下面的内容
 
@ForEach:接收Array<T>类型 
 
Lambda表达式的类型举例
    main(args:Array<String>) 函数 
——    传入参数 Array<String> -> Unit
    println(…)
——传入(Any?可空的any)-> 返回Unit
()->Unit
——无参,返回值Unit
 
(Int)->Int
——传入整型,返回一个整型
 
(String,(String) ->String)->Boolean
——传入字符串,lambda表达式,返回一个boolean
 
println(::printUsage is () -> Unit) :参数为0的Function0 ()————具名函数printUsage()
invoke多少参数就是Function几,最多可以Function0-22
 
lambda表达式的调用
  •     用()进行调用
  •     等价于invoke()
    ex : val sum = {a:Int ,b:int -> a+b}
    // in P1,in P2 out R ,传入两个int返回一个int ,Function0-22均调用invoke(参数列表)
            sum(2,3)  ——————> sum.invoke(2,3)
        
lambda表达式的简化特点
 
  • 函数参数调用时最后一个lambda表达式可以移出去
  • 函数参数只有一个lambda,调用时小括号可以省略
  • lambda只有一个参数可默认为it
  • 入参、返回值与形参一致的函数可以用函数引用的方式作为实参传入(forEach里面传入一个printnln [::println])
 
8、表达式 -中缀表达式、分支表达式、when表达式
    8.1、中缀表达式
只有一个参数,且用infix修饰的函数
    ex: 
        class Book { 
            infix fun on ( place :String){
                …}
        }
        Book() on “My desk”()
 
   8.2、 分支表达式
 
传统的分支使用方法:
    var max = a
        if ( a < b )
            max = b 
    搭配else 的使用:
    var max : Int
        if ( a > b )
            max = a
        else
            max = b 
 
kotlin 中的分支表达式: 
    8.2.1 、if表达式 :
    if…else :
  •    表达式与完备性
  •   if(boolean)
    var x = if (b <0) 0 else b 
   var x = if (b <0) 0//错误 ,赋值时,分支必须完备
 
 if max = if ( a >b ) {
        a
    }
    else {
        b
    }
 
val mode = if (args.isNotEmpty() && args[0] == "1") {
    //if表达式有返回值所以可以做为代码块,每个分支里面的最后一句话是其段的返回值
    DEBUG
} else {
    USER
}
 
注意:
    1、在kotlin中没有没有 java中的这一种写法: true ? 1:0 三元表达式或是说正则,对应的kotlin写法是:
    if( boolean ) 1 else 0
    2、如果if 表达式 只有一个分支, 或者分支结果返回Unit ,其值也是Unit
    3、else 和 else if 同时出现,else要再else if 之后
    4、如果有多条else if 语句同时出现,那么如果有一条else if 语句表达式测试成功,那么就会忽略掉其他所有else if 和else分支
    5、如果出现多个if ,只有一个else情形,那么else子句归属于最内层的if语句
 
 
8.2.2 、when表达式
  •  when表达式 :
        加强版 switch,支持任意类型,不用写break
        支持纯表达式条件分支 类型if
        表达式与完备性
 
when 既可以被当作表达式使用也可以被当作语句使用;当被当作表达式,符合条件的分支的值就是整个表达式的值,如果当作语句使用,则忽略单个分支的值
 
fun pause() {
    when (state) {
        Player.State.BUFFERING -> {}
        Player.State.PAUSED -> {}
        Player.State.PLAYING -> {}
    }
}
fun main(args: Array<String>) {
    val x = 5
    when (x) {
        is Int -> println("Hello $x")
        in 1..100 -> println("$x is in 1..100")
        !in 1..100 -> println("$x is not in 1..100")
        args[0].toInt() -> println("x == args[0)
 
    }
}
注意: 
    1、else 在when 分支里相当于default 其他分支都不满足条会到else分支里
    2、分支处理方式相同,用逗号分隔
    3、分支条件可以是常量也可以是表达式
 
8.3、循环表达式
  8.3.1 、for循环实例: for(element in elements 任何提供迭代器的集合) for{ :. keyword }
 
遍历一个简单的数组 or list :
    for ( i in array.indices )
        println ( array [i] )
等同于 下面的 i in args 
A:
for (arg in args) {  //for ( i in array.indices) {...}   
    println(arg)
}
 
B:库函数 withIndex
for ((index, value) in args.withIndex()) {
    println("$index->$value")
    //0 -> a 1-> b 2-> c 3-> d
}
C:结果和B一模一样
for (indexedValue in args.withIndex()) {
    println("${indexedValue.index}->${indexedValue.value}”)
  //0 -> a 1-> b 2-> c 3-> d
}
 
解析:返回一个IndexValue 类型
public fun <T> Array<out T>.withIndex(): Iterable<IndexedValue<T>> {
    return IndexingIterable { iterator() }
}
public data class IndexedValue<out T>(public val index: Int, public val value: T)
 
8.3.2、 while循环实例:while(…) / do {...} while(…)...
var x = 5
//1、首先判断条件,接着执行循环体
while (x > 0) { 
    println(x)
    x--
}
 
//2、先执行一次循环体,再判断条件是否继续执行
do { 
    println()
    x--
} while (x > 0)
 
8.3.3、 continue、break 跳出循环实例
continue:跳过当前循环 ,直接进入循环体的下次循环;
break:终止最近的封闭循环 用break
 
class Student {
    fun isNotClothedProperly(): Boolean {
        return false
    }
    fun main(args: Array<String>) {
        val you = Student()
        val students = ArrayList<Student>()
        for (students in students) {
            if (students == you)  continue //当条件满足时候 ,跳过循环
            if (students.isNotClothedProperly()) {
                break//当条件满足时候,跳出循环
            }
        }
    }
}
8.3.4、 return 返回
    在java,c语言中,return语句使用我们在常见不过了。虽然在Scala、groovy语言中,函数的返回值可以不需要在显示的用return 来制定,但是我们仍然认为,使用return 的编码风格更加容易阅读
    在kotlin中,除了表达式的值,有返回值的函数都要求显示使用return 来返回其值
 
小示例:
    fun sum(a: Int ,b :Int ) :Int {
        return a + b
    }
 
fun max ( a:Int , b:Int ) :Int {
    if ( a >b ) returna else returnb
}
 
 
or 在kotlin 中可以直接使用 = 符号 直接返回一个函数体的值
 fun sum ( a:Int , b :Int ) = a +b
 fun max ( a:Int , b:Int )   =  if ( a >b ) returna else b
 
 
    注意:
  •  在koltin中 return 语句会从最近的函数 或 匿名函数中 返回,但是在Lambda表达式中遇到return ,则直接返回最近的外层函数
  •  在非全局的返回只支持内部方法,如果我们只是需要跳出内部方法,就必须标记它并且返回这个标签
 
 
ex1:
fun return_1( ){
    val Array1 = intArrayOf( 1,2,3,4,5)
        Array1.forEach {
            if( it ==3 ) return
                println(it)
        }
}
结果:在遇到3的时候会直接返回  println结果: 1 2
 
用一个方法表达式替代内部匿名方法,在方法内部声明一个return{ :. keyword} 将从其内部返回
ex2:
fun return2( ){
    val intArray = intArrayOf (1,2,3,4,5)
    intArray.forEach (
       fun (a:Int ) {
            if (a == 3) return
                println(a)
        })
}
结果:在运行时候遇到3会跳过它继续执行,输出结果:1 2 4 5
 
需要在返回的时候带有一个值:
ex3:
    return @ a1
结果:在标签@a 上返回1,而不是“返回一个带标签的表达式(@a 1)”
 
8.4、 标签 label
在kotlin中 任何表达式都可以用标签来标记。标签的格式为标识符后跟 @ 符号;作用是用标签来控制 return/break/continue的跳转行为
 
     在lambda表达式开头处添加标签here@ ,可以理解为 该标签相当于记录了Lambda入口地址,然后在表达式内部使用了return@here来跳转至lambda表达式该地址处
    接收该Lambda表达式的函数是forEach ,所以可以直接使用return@ForEach,来跳转到此处执行下一轮循环;如果使用break在循环体中,是跳出最外层的循环
  
多层循环嵌套的终止结合标签使用:
        Outter@for (…) {
            Inner@while ( i < 0) {
                if( …) 
               // break  :若没有@Outter 这里是把while循环给break掉,继续执行for循环
               break@Outter  // 将for循环也给break 掉
            }
        }
 
8.5、 throw 
throw 用来标记无返回的函数,它的类型是特殊类型Nothing。该类没有值。和void 类似
fun fail (msg :String) :Noting {
    throw IllegalArgumentException(msg)
}
    throw 表达式赋值给一个变量,需要显示声明类型:Nothing
val ex:Nothing = throw Exception(“xxxxxx")
注意:该ex变量是Noting类型,没有任何值,无法当作参数传递给函数
 
8.6、课后小栗子:
自定义一个迭代器,实现数组的添加or 删除功能
 
fun main(args: Array<String>) {
    val list=MyIntList()
    list.add(1)
    list.add(2)
    list.add(3)
}
class MyIterator(val iterator: Iterator<Int>) {
    operator fun next(): Int {
        return iterator.next()
    }
    operator fun hasNext(): Boolean {
        return iterator.hasNext()
    }
}
class MyIntList {
    private val list = ArrayList<Int>()
    fun add(int: Int) {
        list.add(int)
    }
    fun remove(int: Int) {
        list.remove(int)
    }
    //定义operator 运算符
    operator fun iterator(): MyIterator {
        return MyIterator(list.iterator())
    }
}
 
 
8、异常捕获
try{…}catch(e:xxxx){...}finally{…} 表达式,可以用来赋值
 
val result = try{ … }catch(e:xxx){ … }
 
注意:
    先执行finally 代码在返回 return 
return try { x/y 
        }catch (e: Exception ){
            0
        }finally{
            …
    }
 
9、具名参数、变长参数、默认参数
 具名参数:指定实参的方式 ,在使用的时候需要赋值在括号里(...)
        fun sum( arg1: Int ,arg2: Int ) =arg1+arg2
        sum ( arg1 = 2 ,arg2 =3) //参数位置可以互换
 
变长参数:某个参数可以接受多个值;可以不为最后一个参数;参数时候有歧义需要使用具名参数
 
        args:Array<String> —> varargargs:String 两者完全一样
   hello(1, 2, 3, 4, 5, string = "hello”)
//将hello赋值给string,在这里使用具名参数,将1-5赋值给ints,后面复制给string
 
ex:
fun hello(vararg ints: Int, string: String) {
    ints.forEach(::println)
    println(string)
}
Spread Operator——变长参数场景
特点:
  •  *array :表示把array展开(只支持array数组),变成一个个元素传入使用
  • 该类仅支持变长参数的实参
  • 不能重载
  • 只能使用array数组 ,不能使用list 等其他集合    
 
val array = intArrayOf (1 ,2 ,3,4)
   hello( 3.0, *array ,string = “hello”) 
 
默认参数:可以指定给参数列表的任意一个参数,如果把默认参数指定给一个比较靠前位置的参数,其后面的参数需要使用具名参数的形式表示
 
 
 
一个简单的命令行计算器:
/**
* 一个极简的命令行计数器
* */
fun main(args: Array<String>) {
    while (true) {
        try {
            println("请输入算式数据,比如:1+1")
            val input = readLine() ?: break
            val splits = input.trim().split(" “) //空格分隔符,并去掉string首尾空字符
            if (splits.size < 3) {
                throw IllegalArgumentException("参数个数异常")
            }
            val arg1 = splits[0].toDouble()
            val op = splits[1] //运算符
            val arg2 = splits[2].toDouble()
            println(
        "$arg1 $op $arg2=${
            Operator(op).invoke(arg1, arg2)
        }”
     ) //将op运算符赋值给等式
        } catch (e: NumberFormatException) {
            println("类型格式异常:" + e)
        } catch (e: IllegalArgumentException) {
            println("输入是否为三个参数的一般计算:" + e)
        }
        println("是否再来一次[Y]")
        val cmd = readLine()
        if (cmd == null || cmd.toLowerCase() == "y") {
            println("输入有误请查看")
            break
        }
    }
}
//定义一个运算类
class Operator(op: String) {
    val opFun: (left: Double, right: Double) -> Double
   //初始化opFun
    init {
        opFun = when(op) {
            "+" -> { l, r -> l + r }
            "-" -> { l, r -> l - r }
            "*" -> { l, r -> l * r }
            "/" -> { l, r -> l / r }
            "%" -> { l, r -> l % r }
            else -> {//when表达式的完备性
                throw UnsupportedOperationException(op)
            }
        }
    }
    fun invoke(left: Double, right: Double): Double {
        return opFun(left, right)
    }
}
 
 
Kotlin导出执行程序
在根目录 ,build.gradle中添加依赖:
    apply plugin: ‘application'
    mainClassName = “包名.类名kt”
 
CMD: cd build/instal/下包名
    授予权限:chmod 755 bin/下包名
    bin/下包名
posted @ 2018-09-19 17:00  想不起来的角落  阅读(1195)  评论(0编辑  收藏  举报