Kotlin 學(xué)習(xí)筆記(一)—— 基本類型、函數(shù)、lambda、類與對(duì)象的寫(xiě)法

最近終于要入坑 Kotlin 啦~ 這是系列學(xué)習(xí)筆記的首篇,一起來(lái)學(xué)習(xí)鴨~

1. 基本類型

    var age: Int = 123    // 標(biāo)準(zhǔn)語(yǔ)法,聲明一個(gè)可變變量 age
    val name: String = "Tom"    // 標(biāo)準(zhǔn)語(yǔ)法,聲明一個(gè)不可變變量 name (不可變變量不是常量)
    val sex: String? = null    // 聲明一個(gè)可為空的字符串變量 sex,String 與 String? 不是同種類型

String 和 String? 是兩種類型,前者修飾的變量不可為 null 空值;后者加了 “?” 之后修飾的變量就可以為 null 了,這也是 kotlin 空安全的一種體現(xiàn)。

當(dāng)編譯器可以推斷出變量的數(shù)據(jù)類型時(shí),可以不用寫(xiě)冒號(hào)和后面的數(shù)據(jù)類型,例如:

    var age = 123    // 可推斷出 age 為 Int,所以可不寫(xiě)
    val name = "Tom"    // 可推斷出 name 為 String,所以可不寫(xiě)

雙感嘆號(hào) “!!” 可以強(qiáng)轉(zhuǎn)類型,如下代碼。 name2 是可為空的 String? 類型,直接賦給不可為空的 name1 就會(huì)報(bào)錯(cuò)。如果確定 name2 一定不為空,則可以在后面加上 "!!" 強(qiáng)轉(zhuǎn)。

    var name1: String = "Tom"    // name1 不可為空
    var name2: String? = "Jack";    // name2 可為空
    //name1 = name2    // 報(bào)錯(cuò)
    name1 = name2!!

2. 關(guān)鍵字

  1. open 。被聲明為 open 的 class 是可以被繼承的,這里注意下 kotlin 中一個(gè)類是默認(rèn)被修飾為 final 的,即默認(rèn)的類是不能被繼承的。

3. 函數(shù)

kotlin 函數(shù)前有 fun 關(guān)鍵字,返回值類型要寫(xiě)在入?yún)⒗ㄌ?hào)后和函數(shù)體大括號(hào)前:

fun main() {
    printLen("栗子")
}

fun printLen(str: String): String {
    println("舉個(gè) $str !")    // 這種寫(xiě)法類似于 C 語(yǔ)言了
    return str
}

// Kotlin 函數(shù)參數(shù)還可以設(shè)置默認(rèn)值
fun printLen(str: String = "我是默認(rèn)值~"): String {
    println("舉個(gè) $str !")    // 這種寫(xiě)法類似于 C 語(yǔ)言了
    return str
}

4. Kotlin 方法可直接寫(xiě)在 .kt 文件里,不用寫(xiě)在某個(gè)類中

例如有個(gè) Util.kt 的文件,里面有許多工具類的方法,如果在 Java 中,就必須在類中編寫(xiě)代碼:

public class Utils {
    public static final void echo(String name) {
        println("name = " + name);
    }
}

調(diào)用時(shí),就是:

Utils.echo("Hello UnderWorld! ");

而在 Kotlin 代碼中,可以直接在 Util.kt 文件中這么寫(xiě):

// Util.kt 文件
fun echo(name: String) {
   println("name = $name")
}

在 Java 代碼里調(diào)用就可以直接這么寫(xiě):

// Main.java 文件
public static void main(String[] args) {
    UtilKt.echo("Hello World! ");
}

5. 與 Java 代碼之間的互調(diào)

object Test {    // Kotlin 代碼里 匿名內(nèi)部類 的寫(xiě)法
    fun say(msg: String) {
        println(msg)
    }
}

在 kotlin 代碼中調(diào)用 Test 中的 say 方法:

Test.say("Good Morning~")

在 Java 代碼中調(diào)用,則:

Test.INSTANCE.say("Good Morning~")

在 kotlin 中調(diào)用一個(gè) Java 類,不能像在 Java 中一樣寫(xiě)成這樣: Test.class ,而是要這樣寫(xiě):Test::class.java。另外 Kotlin 類是被編譯為 KClass 文件,而不是 class 文件。所以,在 Kotlin 代碼里,如果要調(diào)用一個(gè) Kotlin 的類,則不用加 .java 后綴,而是直接寫(xiě)成:Util::class。

6. Java 與 Kotlin 之間的沖突解決

  1. 關(guān)鍵字沖突。比如 in 這個(gè)關(guān)鍵字,在 Kotlin 中是一個(gè)關(guān)鍵字,如果要引用 Java 類中一個(gè)叫 in 的對(duì)象時(shí),則需要用反引號(hào) ` 解決這個(gè)沖突:
Utils.`in`   // 在 Utils.java 中,in 是一個(gè)屬性:public static int in = 100;
  1. Kotlin 沒(méi)有封裝類。Kotlin 中沒(méi)有像 Integer 的封裝類,只有 Int 等基本類型,只有通過(guò)反射的方式才能調(diào)用或用于鑒別 Integer 的封裝類類型。
    這里給出幾個(gè)網(wǎng)上應(yīng)用的例子,實(shí)際中使用時(shí),再補(bǔ)充。
    1)在 kotlin 代碼中使用 Integer.class。假如 Java 類中有方法:void func(Class clazz){},那么在 Kotlin 中如果需要傳入一個(gè) Integer.class 該怎么辦?正確的做法是:func(Int::class.javaObjectType),而不是func(Int::class.java)
    2)Int::class.java指向的是 kotlin 標(biāo)準(zhǔn)庫(kù)中的 Int.kt ;Int::class.javaObjectType指向的是 JDK 里的 Integer.java 類。
  2. Kotlin 是空安全的。Kotlin 如果調(diào)用了 Java 中的代碼,則需要用 ***? 的類型來(lái)接收,這樣可以防止空指針異常。例如 Java 中是 String 類型的對(duì)象,要在 Kotlin 中使用的話,需要用 String? 類型來(lái)接收。
  3. Kotlin 沒(méi)有靜態(tài)變量和靜態(tài)方法。沒(méi)有靜態(tài)方法的問(wèn)題,可以在方法前添加 @JvmStatic 注解來(lái)解決:
object Utils {
    @JvmStatic
    fun getName(): String{
        return "hehe"
    }
}

當(dāng)然也可以將方法寫(xiě)在類的 companion object {}中。

7. 擴(kuò)展函數(shù)

kotlin 支持給原有的類添加一些擴(kuò)展的功能,就是通過(guò)擴(kuò)展函數(shù)來(lái)實(shí)現(xiàn)的。可以針對(duì)第三方庫(kù)中對(duì)象添加一些我們需要的方法。例如我們可以擴(kuò)展一下 User 類中的方法:

fun User.getInfo(): String {    // 原本的 User 類中是沒(méi)有 getInfo 方法的
    return uid.toString() + name
}

這樣,我們相當(dāng)于給 User 類添加了一個(gè)方法 getInfo,然后 User 類的對(duì)象都可以調(diào)用 getInfo 方法了。請(qǐng)注意這里的擴(kuò)展函數(shù)是靜態(tài)添加給這個(gè)類的,不具備運(yùn)行時(shí)的多態(tài)的??梢钥聪旅娴拇a:

open class Animal    // 父類
class Dog: Animal()    // 子類

fun Animal.name() = "animal"    // 父類擴(kuò)展函數(shù) name,返回 animal 
fun Dog.name() = "dog"    // 子類擴(kuò)展函數(shù) name,返回 dog

fun Animal.printName(animal: Animal) {    // 父類擴(kuò)展函數(shù) printName,調(diào)用的是父類對(duì)象的 name 函數(shù)
    println(animal.name())
}

fun main(args: Array<String>) {
    Dog().printName(Dog())    // 打印的結(jié)果是 “animal”,這說(shuō)明擴(kuò)展函數(shù)不具備運(yùn)行時(shí)多態(tài)的特點(diǎn)。
}

將這段代碼反編譯成 Java 代碼,可以看到最終調(diào)用的 Dog().printName(Dog()) 這段代碼,被編譯成了 printName((Animal)(new Dog()), (Animal)(new Dog())); ,即最后調(diào)用會(huì)將 Dog 對(duì)象強(qiáng)轉(zhuǎn)為 Animal 對(duì)象,這樣就不具有多態(tài)的特點(diǎn)了。

8. Lambda 閉包

  1. Lambda 閉包聲明,可以為:
// lambda 閉包
val print = {name: String ->    // 閉包名聲明為 print,閉包還允許添加參數(shù),這里聲明了一個(gè) name 的參數(shù)
    println(name)
}

這里閉包中的參數(shù)個(gè)數(shù)是有限制的,上限為 22個(gè)。因?yàn)?Kotlin 只為我們定義了含有 22 個(gè)參數(shù)的 Function22,如圖所示:

圖片.png

如果我們需要用到 23個(gè)參數(shù)的 Lambda 閉包該怎么辦呢?這個(gè)時(shí)候我們就需要手動(dòng)聲明一個(gè)kotlin包中的 Function23。這里需要手動(dòng)定義一個(gè) Java 類的 Function23,因?yàn)橹挥幸粋€(gè)kotlin標(biāo)準(zhǔn)庫(kù)才可以聲明一個(gè)kotlin包名,而我們自己是不能聲明一個(gè)類的包名為kotlin的,但是 Java和kotlin是互通的,所以我們可以將這個(gè)Function23 聲明為一個(gè) Java類,并將它的包名設(shè)置為kotlin,這樣就可以聲明參數(shù)個(gè)數(shù)超過(guò) 22 的閉包了。

package kotlin;

public interface Function23<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, R> extends Function<R> {
    R invoke(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11, P12 p12, P13 p13, P14 p14, P15 p15, P16 p16, P17 p17, P18 p18, P19 p19, P20 p20, P21 p21, P22 p22, P23 p23);
}

9. 高階函數(shù)

高階函數(shù)的特點(diǎn):函數(shù)(Lambda)的參數(shù)也是一個(gè)函數(shù)(Lambda)。
知識(shí)點(diǎn)1:函數(shù)默認(rèn)的返回值為一個(gè) Unit 類型的對(duì)象,可以不寫(xiě)。但如果這個(gè)函數(shù)是作為一個(gè)參數(shù),那么返回類型一定要寫(xiě):

// 只有當(dāng) isDebug 為 true,才會(huì)執(zhí)行后面的 block 函數(shù).  block 函數(shù)是作為一個(gè)參數(shù),所以返回類型要顯式寫(xiě)出
fun Onlyif(isDebug: Boolean, block: () -> Unit) {
    if (isDebug) block()
}

fun main() {
    val runnable = Runnable {
        print("Runnable run!")
    }
    val function: () -> Unit
    function = runnable::run    // Runnable 只有一個(gè) run 方法,所以可以直接用雙冒號(hào)進(jìn)行調(diào)用
    Onlyif(true, function)
}

Kotlin 的 Lambda 會(huì)編譯為一個(gè)匿名內(nèi)部類,可以使用 inline 關(guān)鍵字來(lái)修飾方法,這樣當(dāng)方法在編譯時(shí)就會(huì)拆解方法的調(diào)用為語(yǔ)句調(diào)用,進(jìn)而減少創(chuàng)建不必要的對(duì)象。
但要注意,過(guò)多使用 inline 關(guān)鍵字會(huì)增加編譯器的編譯負(fù)擔(dān),所以 inline 只適合修飾高階函數(shù),例如上述的高階函數(shù)就可以用 inline 修飾:

inline fun Onlyif(isDebug: Boolean, block: () -> Unit) {
    if (isDebug) block()
}

10. 類與對(duì)象

  1. Kotlin 類默認(rèn)是被 public final 修飾的,默認(rèn)的父類是 Any,而不是 Object。
  2. Kotlin 類的構(gòu)造函數(shù)會(huì)默認(rèn)調(diào)用 init 方法,所以可以在 init 方法中執(zhí)行一些初始化的操作:
class TestView: View {
    constructor(context: Context): super(context)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    
    init {
        print("構(gòu)造函數(shù)已執(zhí)行~")
    }
}
  1. 四種訪問(wèn)修飾符,分別是 private、protected、public、internal。前三種與 Java 相同,internal 表示 module 模塊內(nèi)部是都可以訪問(wèn)的,而其他 module 是無(wú)法訪問(wèn)的。
  2. Kotlin 的伴生對(duì)象??梢詫?shí)現(xiàn)靜態(tài)方法和靜態(tài)變量:
class StringUtils {
    // 伴生對(duì)象
    companion object {
        // 伴生對(duì)象實(shí)現(xiàn)靜態(tài)變量
        val TAG = "StringUitls"
        // 伴生對(duì)象實(shí)現(xiàn)靜態(tài)方法
        fun isEmpty(str: String) : Boolean {
            return "" == str
        }
    }
}
  1. Kotlin 中的單例的實(shí)現(xiàn)??梢允褂冒樯鷮?duì)象來(lái)實(shí)現(xiàn) kotlin 的單例:
// 單例實(shí)現(xiàn)
class SingleInstance private constructor() {
    companion object {
        fun get() : SingleInstance {
            return Holder.instance
        }
    }
    
    private object Holder {    // 通過(guò) object 創(chuàng)建一個(gè)匿名內(nèi)部類
        val instance = SingleInstance()
    }
}

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容