Kotlin基礎學習筆記 (三)

2、Kotlin 基礎程序結構
 一、Val和Var
    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.二、 var = variable 臨時變量                                                                                                                                                                                                                                                                  
ex: 
    var x = 「HelloWorld 「 //定義變量
        x = 「 hiWolrd 「  //再次賦值
 
類型推導
    val String = 「hello」 //推導出String類型
    val int = 5 //Int類型
    var x = getString() + 5 //String 類型
 
 二、函數 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()
        }
注意事項
  • 方法要功能單一
  • 函數名顧名思義
  • 參數列表不要太複雜
 
三、類
 
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 修飾的都是屬性,沒有就只是構造方法的參數
 
一、val 沒有set()方法由於是不可變的final的
二、 屬性的初始化儘可能在構造方法中完成。
三、沒法在構造方法中完成初始化,嘗試降級爲局部變量
四、var用lateinit延遲初始化,val用lazy
五、可空類型謹慎用null直接初始化 (!!or?)
 
四、修飾符
 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"
 
五、關鍵字 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
        }
    }
}
 
六、基本運算(操做)符
  • 運算符方法重載的定義,任何類能夠定義或是重載基本運算符
  • 經過運算符對應的具名函數來定義,要求方法名字必須一致對的上,fun前面加上operator 關鍵字
  • 參數的個數對的上,類型和返回類型不做要求
  • 不能像Scala同樣定義任意運算符
 
6.一、操做符優先級
    級別由上到下 ,由高到低
        後綴 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++ 
        一、肯定a的類型 ,令其解析爲T
        二、查找一個適用於類型爲T的接收者的、帶有operator修飾符的無參數函數inc()
        三、檢查函數的返回類型是T的子類型
 
    表達式計算步驟:
        一、把a的初始值存儲到臨時存儲a _ 中
        二、把a.inc ( ) 結果賦值給 a
        三、把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.六、相等 與 不等操做符
  •  引用相等 "===" , " !==「  ————》兩個引用指向同一對象————〉不能重載!!!
  •  結構相等 「 == 「 , 「 != 「  ————> 使用 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裏常見 中綴表達式替換.()方法
    }
}
 
 七、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])
 
八、表達式 -中綴表達式、分支表達式、when表達式
    8.一、中綴表達式
只有一個參數,且用infix修飾的函數
    ex: 
        class Book { 
            infix fun on ( place :String){
                …}
        }
        Book() on 「My desk」()
 
   8.二、 分支表達式
 
傳統的分支使用方法:
    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
}
 
注意:
    一、在kotlin中沒有沒有 java中的這一種寫法: true ? 1:0 三元表達式或是說正則,對應的kotlin寫法是:
    if( boolean ) 1 else 0
    二、若是if 表達式 只有一個分支, 或者分支結果返回Unit ,其值也是Unit
    三、else 和 else if 同時出現,else要再else if 以後
    四、若是有多條else if 語句同時出現,那麼若是有一條else if 語句表達式測試成功,那麼就會忽略掉其餘全部else if 和else分支
    五、若是出現多個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)
 
    }
}
注意: 
    一、else 在when 分支裏至關於default 其餘分支都不知足條會到else分支裏
    二、分支處理方式相同,用逗號分隔
    三、分支條件能夠是常量也能夠是表達式
 
8.三、循環表達式
  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.二、 while循環實例:while(…) / do {...} while(…)...
var x = 5
//一、首先判斷條件,接着執行循環體
while (x > 0) { 
    println(x)
    x--
}
 
//二、先執行一次循環體,再判斷條件是否繼續執行
do { 
    println()
    x--
} while (x > 0)
 
8.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.四、 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.四、 標籤 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.五、 throw 
throw 用來標記無返回的函數,它的類型是特殊類型Nothing。該類沒有值。和void 相似
fun fail (msg :String) :Noting {
    throw IllegalArgumentException(msg)
}
    throw 表達式賦值給一個變量,須要顯示聲明類型:Nothing
val ex:Nothing = throw Exception(「xxxxxx")
注意:該ex變量是Noting類型,沒有任何值,沒法看成參數傳遞給函數
 
8.六、課後小栗子:
自定義一個迭代器,實現數組的添加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())
    }
}
 
 
八、異常捕獲
try{…}catch(e:xxxx){...}finally{…} 表達式,能夠用來賦值
 
val result = try{ … }catch(e:xxx){ … }
 
注意:
    先執行finally 代碼在返回 return 
return try { x/y 
        }catch (e: Exception ){
            0
        }finally{
            …
    }
 
九、具名參數、變長參數、默認參數
 具名參數:指定實參的方式 ,在使用的時候須要賦值在括號裏(...)
        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/下包名
相關文章
相關標籤/搜索