第一章 Kotlin基礎(chǔ)語(yǔ)法

一、常量與變量(val,var)

  • 1.什么是常量?

1 .val = value ,值類型;
2.類似Java的final;
3.不能重復(fù)賦值;
4.舉例:
運(yùn)行時(shí)常量:val x = getX(); 也就是程序運(yùn)行時(shí)才賦值;
編譯期常量:const val x = 2 ;也就是在編譯時(shí)期就賦值了;

  • 2.什么是變量?

1.var=variable ;
2.可以再次賦值;
3.舉例:
var x = "HelloWorld"http://定義變量
x = "HiWorld"http://再次賦值

示例如下:

/**
 * 常量
 * 帶const為編譯期的常量
 * 不帶const的val為運(yùn)行期常量
 * 不能再次賦值
 */
const val FINAL_HELLO_WORLD:String = "Hello world"

//變量 能夠再次賦值
var helloworld:String = FINAL_HELLO_WORLD

二、類型推導(dǎo)

val string = "Hello"http://推導(dǎo)出String類型
val int = 5 //Int類型
var x = getString()+5 //String類型,加號(hào)在這里是連接符

三、函數(shù)--單一原則:

1.任何函數(shù)都是以fun開(kāi)頭的,緊接著的是方法名字,緊接著的是參數(shù)列表,緊接著的是返回值,緊接著是函數(shù)體;
fun <MethodName>(<ParamsName>:<ParamsType>):<returnValue>{}
2.如果無(wú)返回值,可以寫:Unit或者在參數(shù)列表后什么都寫;Unit類型Java中的void;
3.如果參數(shù)有多個(gè),以逗號(hào)分開(kāi);
4.如果一個(gè)函數(shù)只是返回一個(gè)函數(shù)的表達(dá)式的值,直接可以等于表達(dá)式即可;
5.函數(shù)的名字不是必須的,如果有個(gè)變量接受返回值即可編譯通過(guò);--請(qǐng)看下面的例子
6.函數(shù)作為類型的一種出現(xiàn)的,可以被用來(lái)賦值與傳遞的;

/**
 * 函數(shù)
 * methodName
 * param1,param2是參數(shù)名
 * String Int 是參數(shù)類型
 * 備注:
 * 1.任何函數(shù)都是以fun開(kāi)頭的;
 * 2.如果參數(shù)有多個(gè),以逗號(hào)分開(kāi);
 * 3沒(méi)有返回值在參數(shù)后添加:Unit,也可以不寫,這個(gè)Unit相當(dāng)于java里面的void;
 * 4如果一個(gè)函數(shù)只是返回一個(gè)函數(shù)的表達(dá)式的值,直接可以等于表達(dá)式即可;
 */
fun methodName(param1:String,param2:Int):Unit {

}

//帶返回值的函數(shù)
fun testMethod(args:String):String{
    return args
}

/**
 * 如果一個(gè)函數(shù)只是返回一個(gè)函數(shù)的表達(dá)式的值,直接可以等于表達(dá)式即可
 */
fun returnFun0(param1: Int, param2: Int):Int {
    return param1+param2
}
//上面的等價(jià)于下面的寫法:
fun returnFun(param1:Int,param2:Int) = param1+param2

/**
 * 函數(shù)可以沒(méi)有名字的,如果有一個(gè)變量來(lái)接收的話即可
 */
val resultValue = fun(x: Int):Int {
    return x
}

四、Lambda表達(dá)式

1.也就是匿名函數(shù);函數(shù)作為類型的一種出現(xiàn)的,可以被用來(lái)賦值與傳遞的;
2.寫法:{[參數(shù)列表] ->[函數(shù)體,最后一行是返回值]}
3.() -> Unit
--無(wú)參,返回值為Unit;
4.(Int) -> Int
--有參-傳入Int類型,返回一個(gè)整數(shù);
5.(String,(String) -> String) -> Boolean
--傳入字符串、Lambda表達(dá)式,返回Boolean;

  • 1.Lambda表達(dá)式的調(diào)用

1.用括號(hào)()來(lái)表示方法的調(diào)用,是Kotlin中的運(yùn)算符,等價(jià)于invoke();這個(gè)invoke其實(shí)是運(yùn)算符重載的方法
2.Lambda表達(dá)式如果沒(méi)有參數(shù),箭頭 (->)就不用寫了;
3.Lambda表達(dá)式不是只有一行的,它的返回值是它表達(dá)式的最后一行的值;

val sum = {a:Int,b:Int ->a+b}
//用括號(hào)()來(lái)調(diào)用,等價(jià)于invoke();
sum(2,3)
或者使用:sum.invoke(2,3),二者完全等價(jià);

//Lambda表達(dá)式?jīng)]有參數(shù),箭頭可以不寫,如下:
val printHello = {
  println("hello")//相當(dāng)于函數(shù)的函數(shù)體
}

//Lambda表達(dá)式不是只有一行的,它的返回值是它表達(dá)式的最后一行的值
val sum = {a:Int,b:Int ->
  println("$a + $b = ${a+b}")//這一行也是表達(dá)式,不過(guò)返回的是Unit類型
  a+b//這一行最為L(zhǎng)ambda的最后一行,它的值也是Lambda的返回值;
}
  • 2.Lambda表達(dá)式的簡(jiǎn)化
  • 1.函數(shù)參數(shù)調(diào)用時(shí)最后一個(gè)Lambda可以移出去;也就是說(shuō)如果一個(gè)函數(shù)調(diào)用時(shí),最后一個(gè)參數(shù)是Lambda表達(dá)式,我們傳入?yún)?shù)時(shí),那么這個(gè)參數(shù)可以被移出去;
  • 2.函數(shù)的參數(shù)只有一個(gè)Lambda,調(diào)用時(shí)小括號(hào)可以省略;
  • 3.如果一個(gè)Lambda只有一個(gè)參數(shù),可以不寫,可默認(rèn)為it;
  • 4.入?yún)ⅰ⒎祷刂蹬c形參一致的函數(shù)可以用函數(shù)引用的方式作為實(shí)參傳入;
val sum = {a:Int,b:Int ->a+b}
//Lambda表達(dá)式在沒(méi)有參數(shù)時(shí),箭頭就不用寫了,如下:
//如下面的寫法,因?yàn)闆](méi)有參數(shù),箭頭就不用寫了:
val printHello = {
   println("hello")//相當(dāng)于函數(shù)的函數(shù)體
}

//看下面的數(shù)組遍歷:
arg.forEach{element -> println(element) }
//如果一個(gè)Lambda傳入的參數(shù)只有一個(gè),可以不寫,可默認(rèn)為it;上面的等價(jià)于:
arg.forEach{ println(it) }
//函數(shù)的參數(shù)只有一個(gè)Lambda表達(dá)式時(shí),調(diào)用時(shí)可以將大括號(hào)移到小括號(hào)外面;
//上面的等價(jià)于下面的:
arg.forEach(){println(it)}
//這個(gè)小括號(hào)沒(méi)有什么用,可以把這個(gè)小括號(hào)省略
arg.forEach{println(it)}
//如果函數(shù)的參數(shù)和返回值與action的參數(shù)和返回類型一致,就可以用函數(shù)引用的方式,如下:
arg.forEach(::println)

五、循環(huán)語(yǔ)句

for循環(huán)如下:


    for (i in args) {
        println("輸出的是:$i")
    }


    for ((index, value) in args.withIndex()) {
        println("$index -> $value")
    }

    for (indexValue in args.withIndex()) {
        println("${indexValue.index}---->${indexValue.value}")
    }
//或者使用下面的方式
   args.forEach{
     println(it)
   }
}

//看下面的一個(gè)方法,return會(huì)把函數(shù)截?cái)啵瑹o(wú)法執(zhí)行到最后一行
fun main(args: Array<String>) {

    args.forEach {
        if (it == "c") return
        println(it)
    }
    //你會(huì)發(fā)現(xiàn)如果上面的if條件觸發(fā)了 ,將跳出for循環(huán),并且執(zhí)行不到下面的代碼,原因是:
    //上面的是表達(dá)式,而不是一個(gè)簡(jiǎn)單的遍歷,return會(huì)調(diào)出整個(gè)函數(shù)的執(zhí)行
    println("The End")
}
//如果想只跳出it=="c"這個(gè)條件,循環(huán)繼續(xù)進(jìn)行,整個(gè)函數(shù)執(zhí)行完畢,可以如下:
fun main(args: Array<String>) {

    args.forEach ForEach@ {
        if (it == "c") return@ForEach
        println(it)
    }
    println("The End")
}

while循環(huán)

var int = 5 
    while (int > 0) {
        println("int---->$int")
        int-- 
    }
    
    do {
        println("int---->$int")
        int--
    }while (int>5)

如何跳出 或 跳過(guò)循環(huán)?
跳出或終止循環(huán):break
跳過(guò)循環(huán):continue
多層嵌套循環(huán)的終止需要結(jié)合標(biāo)簽來(lái)實(shí)現(xiàn)

 Outter@ for (i in args) {

        Inner@while (i.toInt() > 0) {
            break@Outter  //這里只是跳出外層的for循環(huán) break和@Outter之間不能有空格
        }
    }

六、異常捕獲(try catch finally)

kotlin里面的異常捕獲和Java中的異常捕獲用法一樣,最后都會(huì)調(diào)用finally;但Kotlin中的try、catch也是表達(dá)式,這點(diǎn)與Java中的不一樣,它可以有返回值,返回值是表達(dá)式的最后一行的值;

  • catch分支匹配異常類型;
  • 它是一個(gè)表達(dá)式,可以用來(lái)賦值,與if else when 表達(dá)式是完全類似的;
  • finally 無(wú)論try catch 是否拋出異常都會(huì)執(zhí)行到;

注意此行代碼代表的含義:
return try{ x/y } catch(e:Exception){ 0 } finally{...}
代表:先執(zhí)行finally里面的代碼,再返回;如果無(wú)異常,返回x/y;如果有異常,返回0;

  //kotlin中的try catch也是表達(dá)式,可以有返回值;返回值是表達(dá)式的最后一行的值;
  val result = try {
        args[0]
    } catch (e: Exception) {
        println("jinlaile")
        0
    } finally {
        println("進(jìn)入finally代碼塊")
    }
    println(result)

輸出結(jié)果:
jinlaile
進(jìn)入finally代碼塊
0

七、成員方法、成員變量

  • 屬性:或者說(shuō)是成員變量,類范圍內(nèi)的變量;構(gòu)造方法中val/var修飾的都是屬性;類內(nèi)部也是可以定義屬性的的;
/**
 * aField和anotherField是類Hello的兩個(gè)屬性;
 * 而notField不是屬性,只是普通構(gòu)造方法的參數(shù);
 */
class Hello(val aField: Int, notField: Int){
    val anotherField:String = "hello"
}
  • 方法:或者說(shuō)是成員函數(shù),類范圍內(nèi)的函數(shù);
    在kotlin的類中,自動(dòng)為var修飾成員變量實(shí)現(xiàn)了get、set方法,val修飾的只有g(shù)et方法,因?yàn)関al修飾的是不可變的;如果想要在get、set方法中注入部分邏輯,需要我們重寫其get、set方法,如下;
/**
 * Created by serenitynanian on 2017/6/27.
 */
class ClassMemberAndField {

    //默認(rèn)訪問(wèn)控制是public
    var b = 0

    get() {
        println("執(zhí)行打印邏輯")
        //這個(gè)filed其實(shí)是上面b后真正的值0,這個(gè)filed只有在get、set方法中才能訪問(wèn)的到
        return field
    }
        /**
         * 類型java中的set方法
         * 
         * public void setB(int b){
         *      
         *      this.b = b ;
         * }
         */
    set(value) {
        println("執(zhí)行打印邏輯")
        field = value
    }
}

如果想要控制get、set的訪問(wèn)權(quán)限,可以在get、set方法前加訪問(wèn)限制符,比如protected

  protected set(value) {
            println("logic")
        field = value
    }

如果說(shuō)get、set中沒(méi)有部分邏輯,只是想要修改訪問(wèn)控制符,只需要如下寫即可:

class ClassMemberAndField {

    //默認(rèn)訪問(wèn)控制是public
    protected var b = 0

//    get() {
//        //這個(gè)filed其實(shí)是上面b后真正的值0,這個(gè)filed只有在get、set方法中才能訪問(wèn)的到
//        return field
//    }
//        /**
//         * 類型java中的set方法
//         *
//         * public void setB(int b){
//         *
//         *      this.b = b ;
//         * }
//         */
//    protected set(value) {
//            println("logic")
//        field = value
//    }
   
//如果只想要控制訪問(wèn)權(quán)限,只需要這樣寫即可:
    protected get
    
    protected set

}

在kotlin中聲明成員變量時(shí),編譯器提醒你必須初始化,否則報(bào)錯(cuò);與java不同的時(shí),java會(huì)自動(dòng)給你初始化初始值;但初始值也占內(nèi)存的,所以有時(shí)我們只需要在使用時(shí)才想初始化,這樣怎么辦呢?
只需要在聲明成員變量時(shí)指定一個(gè)lateinit特殊字符就行了;但是在使用之前必須進(jìn)行初始化,否則報(bào)錯(cuò);

var age:Int = 0 
    //如果不指定lateinit 編譯器會(huì)提醒必須初始化或者abstract,
    //指定后就是告訴編譯器,我后面會(huì)進(jìn)行初始化;
    //如果你在初始化它之前就使用這個(gè)變量,會(huì)報(bào)錯(cuò)的;
    lateinit var name:String

但是lateinit只能放在var修飾的可變變量前面,不能放在val修飾的變量前面;如果想要達(dá)到延遲初始化必須使用delegate,如下:

   /**
     * lazy需要傳入一個(gè)lambda表達(dá)式,一個(gè)無(wú)參的表達(dá)式返回一個(gè)T類型,
     * 這個(gè)T類型就是下面的String類型;
     */
   val address:String by lazy {
       "dsfa"
   }

總結(jié)下屬性訪問(wèn)控制:
var修飾的有g(shù)et、set方法;
val修飾的只有g(shù)et方法,沒(méi)有set方法,因?yàn)関al修飾的變量是不可變的;

總結(jié)下屬性初始化:
屬性的初始化盡量在構(gòu)造方法中進(jìn)行初始化完成;
無(wú)法在構(gòu)造方法中初始化,嘗試降級(jí)為局部變量;
var用lateinit延遲初始化,val用lazy;
可空類型謹(jǐn)慎使用null直接初始化;

八、表達(dá)式(中綴表達(dá)式、分支表達(dá)式、when表達(dá)式)

  • 中綴表達(dá)式
  • 只有一個(gè)參數(shù),且用infix修飾的函數(shù);
class Book {
   infix fun on(place: String) {

    }
}

fun main(args: Array<String>) {
    //函數(shù)前沒(méi)有使用infix關(guān)鍵字,必須像平常調(diào)用函數(shù)一樣使用
    Book().on("my desk")
    
    //如果函數(shù)前面使用了infix,不用在方法前加. 不用在方法后加括號(hào)
    Book() on "my dest"
}
  • 2.使用了中綴表達(dá)式后,在調(diào)用時(shí),可以直接使用[對(duì)象] <函數(shù)名> [參數(shù)對(duì)象] 如上示例;

  • 分支表達(dá)式(if),而不只是分支語(yǔ)句
    分支表達(dá)式是可以有返回值的,返回值是分支語(yǔ)句中最后一行的值 ;

private const val DEBUG = 1
private const val USER = 0
fun main(args: Array<String>) {

//    var mode = USER
//    if (args.isNotEmpty() && args[0] == "1") {
//        mode = DEBUG
//    }

    /**
     * 從下面可以看出來(lái)if可不只是一個(gè)簡(jiǎn)單的分支語(yǔ)句,
     * 它還是一個(gè)表達(dá)式,它是有返回值的,返回值是是分支表達(dá)式中最后一行的值;
     * 在上面的分支語(yǔ)句中,只能定義var的mode,因?yàn)楹竺嬉薷模?     * 如果使用分支表達(dá)式,就直接可以使用val修飾的mode了,使用如下:
     */
    val mode =
            if (args.isNotEmpty() && args[0] == "1") {
                DEBUG
            }else{
                USER
            }

    println("請(qǐng)輸入用戶名:")
    val username = readLine()
    println("請(qǐng)輸入密碼:")
    val password = readLine()

    if (mode == DEBUG && username == USERNAME && password == PASSWORD) {
        println("超管登錄成功")
    } else if (username == USERNAME && password == PASSWORD) {
        println("普通用戶登錄成功")
    } else {
        println("登錄失敗")
    }

}
  • when表達(dá)式
    類似Java中的switch語(yǔ)句,由于case語(yǔ)句中只能判斷String,enum,byte,int等,并且在每個(gè)分支語(yǔ)句中都要寫break;在kotlin中沒(méi)有了switch語(yǔ)句了,使用了when表達(dá)式來(lái)代替了;
    public void pause() {
        switch (state) {
            case BUFFERING:
            case PLAYING:
                doPause();
                break;
            default:
                //什么都不做
        }
    }

//下面是kotlin中的when表達(dá)式
    fun pause() {
        when (state) {
            PlayerKt.State.BUFFERING, PlayerKt.State.PLAYING -> doPause()
            else -> {

            }
        }
    }

when分支表達(dá)式只要第一個(gè)分支語(yǔ)句執(zhí)行了,不用使用break,后面的分支語(yǔ)句就不會(huì)執(zhí)行了

fun main(args: Array<String>) {
    var x = 5
    //when表達(dá)式只要第一個(gè)分支語(yǔ)句執(zhí)行了,不用使用break,后面的就不會(huì)執(zhí)行了
    when (x) {
        is Int -> println("Hello $x")
        in 1..100 -> println("$x is in 1..100")//x的值是否在[1,100]
        !in 1..100 -> println("$x is not in 1..100")//x的值不在[1,100]
        in 1 until 5 -> println("$x is in[0,5)")//x的值是否在[0,5)之間,取不到5
        args[0].toInt() -> println("x == args[0]")//這個(gè)是說(shuō)args[0]是否與x一樣
    }
}

//when與if一樣,也有返回值,都是每一個(gè)分支最后一行表達(dá)式的值;
    val mode = when {
        args.isNotEmpty()&& args[0] == "1" -> 1
        else -> 0
    }

表達(dá)式總結(jié):

  • if ... else ...這個(gè)用法基本和Java中一致;
  • 表達(dá)式必須具備完整性,如果只有if沒(méi)有else編譯報(bào)錯(cuò);
 var b = if (mode == 0) {
        0
    } 
    //如果沒(méi)有下面的分支語(yǔ)句,編譯報(bào)錯(cuò)
    //賦值時(shí),分支必須完備
    else {
        1
    }
  • when表達(dá)式,加強(qiáng)版的switch,支持任意類型,也不用寫break;
    支持純表達(dá)式條件分支(類似if)
   val mode = when {
       args.isNotEmpty()&& args[0] == "1" -> 1
       else -> 0
   }

必須具備完備性;

九、具名參數(shù),變長(zhǎng)參數(shù),默認(rèn)參數(shù)

  • 具名參數(shù):就是在給函數(shù)傳入實(shí)參的時(shí)候,把形參也賦值上,也就是下面在調(diào)用sum函數(shù)時(shí),在傳入實(shí)參的時(shí)候,把a(bǔ)rg1 和 arg2也附帶上(arg1 = 1,arg2 = 2);

具名參數(shù)的順序可以不是固定的;

class ForDemo {

    fun sum(arg1:Int,arg2:Int) = arg1 + arg2
}

fun main(args: Array<String>) {


    var forDemo = ForDemo()
    /**
     * 在使用函數(shù)傳入實(shí)參的時(shí)候,把形參也賦上;
     * 因?yàn)槭蔷呙麉?shù),所以形參的順序可以不固定
     * 也就是明確告訴編譯器這個(gè)2是給arg2的,1是給arg1的
     */
    forDemo.sum(arg2 = 2, arg1 = 1)

}
  • 變長(zhǎng)參數(shù)
    某個(gè)參數(shù)可以接收多個(gè)值;
    變長(zhǎng)參數(shù)可以不為最后一個(gè)參數(shù);但是傳入實(shí)參時(shí),可以放在最后用具名參數(shù)表示即可;
    如果傳參有歧義,需要使用具名參數(shù)指定某個(gè)特定參數(shù);
    最常見(jiàn)的就是main函數(shù),它里面是個(gè)數(shù)組,我們可以將它改變?yōu)樽冮L(zhǎng)參數(shù);它的使用和數(shù)組完全一樣
   var array = intArrayOf(1,23,43,25,56)

    hello(3.0,1,123,321,2,string = "hello")
    //*:Spread Operator這個(gè)符號(hào)現(xiàn)在只支持變長(zhǎng)參數(shù),且只支持array,不支持list
    hello(3.0,*array,string = "hello")

fun hello(double:Double,vararg ints: Int,string:String) {
    ints.forEach { ::println }
}

在Java中變長(zhǎng)參數(shù)只能是函數(shù)的最后一個(gè)參數(shù),否則編譯器無(wú)法識(shí)別的;
在kotlin中因?yàn)榇嬖诰呙瘮?shù),因此它可以放在任意位置;在傳入實(shí)參的時(shí)候可以用具名函數(shù)放在最后一個(gè)位置;

*:Spread Operator 這個(gè)不是一般的運(yùn)算符;
只支持展開(kāi)Array;
只用于變長(zhǎng)參數(shù)列表的實(shí)參;
不能重載;

  • 默認(rèn)參數(shù)
    1.就是在函數(shù)編寫時(shí),可以給函數(shù)任意位置的形參一個(gè)默認(rèn)值;在實(shí)際調(diào)用函數(shù)時(shí),這個(gè)參數(shù)可以不傳;
    2.但是如果該默認(rèn)參數(shù)不是最后一個(gè),其他參數(shù)使用具名參數(shù)即可,不用寫默認(rèn)的;
    3.傳參出現(xiàn)歧義時(shí),也就是編譯器報(bào)錯(cuò),需要使用具名函數(shù),具體請(qǐng)看第二條;
    hello(3.0,1,123,321,2,string = "hello")
    //* 這個(gè)符號(hào)現(xiàn)在只支持變長(zhǎng)參數(shù),且只支持array,不支持list
    hello(ints = *array,string = "hello")

fun hello(double:Double = 3.0 ,vararg ints: Int,string:String) {
    ints.forEach { ::println }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容