Hello Kotlin

Java轉(zhuǎn)Kotlin,對語法、聲明和一些符號的使用不是很清楚,特此記錄

變量定義方式

val 只讀變量,只能賦值一次
var 讀寫變量,可多次賦值

val a: Int = 1  // 立即賦值
val b = 2   // 自動(dòng)推斷出 `Int` 類型
val c: Int  // 如果沒有初始值類型不能省略
c = 3       // 明確賦值

理解自動(dòng)類型推斷

函數(shù)的定義

//帶有兩個(gè) Int 參數(shù)、返回 Int 的函數(shù):
fun sum(a: Int, b: Int): Int {
    return a + b
}

//將表達(dá)式作為函數(shù)體、返回值類型自動(dòng)推斷的函數(shù):
fun sum(a: Int, b: Int) = a + b

//將表達(dá)式作為函數(shù)體、返回值類型自動(dòng)推斷的函數(shù):
fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

//Unit 返回類型可以省略:
fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

模板字符串

在字符串中引用變量,或者在字符串中使用任意表達(dá)式

var a = 1
val s1 = "a is $a1"

a = 2
val s2 = "${s1.replace("is", "was")}, but now is $a"

s2的輸出結(jié):a was 1, but now is 2

表達(dá)式

與Java類似,if(){}if(){}else{}

fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}

fun maxOf(a: Int, b: Int) = if (a > b) a else b

兩種寫法都一樣

空值檢測和和使用

當(dāng)某個(gè)變量的值可以為 null 的時(shí)候,必須在聲明處的類型后添加 ? 來標(biāo)識該引用可為空

fun parseInt(str: String): Int? {
    //表示該方法可以返回一個(gè)整型或者null
}

使用類型檢測及自動(dòng)類型轉(zhuǎn)換

is 運(yùn)算符檢測一個(gè)表達(dá)式是否某類型的一個(gè)實(shí)例。 如果一個(gè)不可變的局部變量或?qū)傩砸呀?jīng)判斷出為某類型,那么檢測后的分支中可以直接當(dāng)作該類型使用,無需顯式轉(zhuǎn)換:

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` 在該條件分支內(nèi)自動(dòng)轉(zhuǎn)換成 `String`
        return obj.length
    }

    // 在離開類型檢測分支后,`obj` 仍然是 `Any` 類型
    return null
}

fun getStringLength(obj: Any): Int? {
    // `obj` 在 `&&` 右邊自動(dòng)轉(zhuǎn)換成 `String` 類型
    if (obj is String && obj.length > 0) {
      return obj.length
    }

    return null
}

emmmm.....這里的is和Java的instanceof是一個(gè)意思,其中is作用范圍內(nèi)自動(dòng)轉(zhuǎn)換數(shù)據(jù)類型比較Java方便了不少

if條件表達(dá)式

// 傳統(tǒng)用法
var max = a 
if (a < b) max = b

// With else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// 作為表達(dá)式
val max = if (a > b) a else b

//if的分支可以是代碼塊,最后的表達(dá)式作為該塊的值
val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

與Java的if不同在于,Kotlin的if可以作為語句,也可一作為表達(dá)式,當(dāng)它作為表達(dá)式時(shí),每一個(gè)分支中最后一個(gè)表達(dá)式的值為分支的值

When表達(dá)式

Kotlin使用when取代了switch

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意這個(gè)塊
        print("x is neither 1 nor 2")
    }
}

//多分支合并,和switch真的很像
when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

//任意表達(dá)是作為分支
when (x) {
    parseInt(s) -> print("s encodes x")
    else -> print("s does not encode x")
}

//任意區(qū)間判斷作為分支
when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

//Since Kotlin 1.3, it is possible to capture when subject in a variable using following syntax:
//強(qiáng)大到令人發(fā)指??
fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }

和if一樣,when也可以作為表示式使用,但是需要注意:如果 when 作為一個(gè)表達(dá)式使用,則必須有 else 分支, 除非編譯器能夠檢測出所有的可能情況都已經(jīng)覆蓋了

伴生對象

類內(nèi)部的對象聲明可以用 companion 關(guān)鍵字標(biāo)記

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

該伴生對象的成員可通過只使用類名作為限定符來調(diào)用:

val instance = MyClass.create()

其自身所用的類的名稱(不是另一個(gè)名稱的限定符)可用作對該類的伴生對象 (無論是否命名)的引用

class MyClass1 {
    companion object Named { }
}

val x = MyClass1

class MyClass2 {
    companion object { }
}

val y = MyClass2

emmm...看起來很像Java的static,但是它不是static,請注意,即使伴生對象的成員看起來像其他語言的靜態(tài)成員,在運(yùn)行時(shí)他們?nèi)匀皇钦鎸?shí)對象的實(shí)例成員,而且,例如還可以實(shí)現(xiàn)接口

interface Factory<T> {
    fun create(): T
}

class MyClass {
    companion object : Factory<MyClass> {
        override fun create(): MyClass = MyClass()
    }
}

val f: Factory<MyClass> = MyClass

對象表達(dá)式和對象聲明之間有一個(gè)重要的語義差別:
對象表達(dá)式是在使用他們的地方**立即**執(zhí)行(及初始化)的
對象聲明是在第一次被訪問到時(shí)**延遲**初始化的
伴生對象的初始化是在相應(yīng)的類被加載(解析)時(shí),與 Java 靜態(tài)初始化器的語義相匹配

創(chuàng)建基本類和實(shí)例

不需要關(guān)鍵字new,直接類名稱就能創(chuàng)建一個(gè)對象

val rectangle = Rectangle(5.0, 2.0) // 不需要“new”關(guān)鍵字
val triangle = Triangle(3.0, 4.0, 5.0)

主構(gòu)造函數(shù)和次構(gòu)造函數(shù)以及初始化

在 Kotlin 中的一個(gè)類可以有一個(gè)主構(gòu)造函數(shù)以及一個(gè)或多個(gè)次構(gòu)造函數(shù)。主構(gòu)造函數(shù)是類頭的一部分:它跟在類名(與可選的類型參數(shù))后

class Person constructor(firstName: String) { ... }

//如果主構(gòu)造函數(shù)沒有任何注解或者可見性修飾符,可以省略這個(gè) constructor 關(guān)鍵字
class Person(firstName: String) { ... }

//事實(shí)上,聲明屬性以及從主構(gòu)造函數(shù)初始化屬性,Kotlin 有簡潔的語法:
class Person(val firstName: String, val lastName: String, var age: Int) { …… }

主構(gòu)造函數(shù)不能包含任何的代碼
初始化代碼應(yīng)該放在init{}

在實(shí)例初始化期間,初始化塊按照它們出現(xiàn)在類體中的順序執(zhí)行,與屬性初始化器交織在一起

class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)
    
    init {
        println("First initializer block that prints ${name}")
    }
    
    val secondProperty = "Second property: ${name.length}".also(::println)
    
    init {
        println("Second initializer block that prints ${name.length}")
    }
}

fun main() {
    InitOrderDemo("hello")
}

First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5

如果一個(gè)非抽象類沒有聲明任何(主或次)構(gòu)造函數(shù),它會(huì)有一個(gè)生成的不帶參數(shù)的主構(gòu)造函數(shù)。構(gòu)造函數(shù)的可見性是 public
這一點(diǎn)和Java挺像的

類也可以聲明前綴有 constructor的次構(gòu)造函數(shù)

class Person {
    constructor(parent: Person) {
        parent.children.add(this)
    }
}

如果類有一個(gè)主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)需要委托給主構(gòu)造函數(shù), 可以直接委托或者通過別的次構(gòu)造函數(shù)間接委托。委托到同一個(gè)類的另一個(gè)構(gòu)造函數(shù)用 this 關(guān)鍵字即可

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

屬性、訪問器、幕后字段、幕后屬性

這里我看api文檔是真的沒有看懂。。。。。。感謝依然范特希西大佬
Kotlin 什么是幕后字段?

接口

使用關(guān)鍵字 interface 來定義接口
Kotlin 的接口與 Java 8 類似,既包含抽象方法的聲明,也包含實(shí)現(xiàn)。
需要注意實(shí)現(xiàn)多個(gè)接口沖突的覆蓋

//接口的定義
interface MyInterface {
    fun bar()
    fun foo() {
      // 可選的方法體
    }
}


/**在接口中聲明的屬性要么是抽象的,要么提*供訪問器的實(shí)現(xiàn)。
在接口中聲明的屬性不能有幕后字段(backingfield),
因此接口中聲明的訪問器不能引用它們*/
interface MyInterface {
    val prop: Int // 抽象的

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}


//接口沖突的覆蓋和重寫
class Child : MyInterface {
    override val prop: Int = 29
}

interface A {
    fun foo() { print("A") }
    fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A {
    override fun bar() { print("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

接口里面不僅是聲明方法,還可以定義方法體。。。但是和抽象類還是有很大的區(qū)別,與抽象類不同的是,接口無法保存狀態(tài)。它可以有屬性但必須聲明為抽象或提供訪問器實(shí)現(xiàn)。

訪問修飾符

  • 如果你不指定任何可見性修飾符,默認(rèn)為 public,這意味著你的聲明將隨處可見;
  • 如果你聲明為 private,它只會(huì)在聲明它的文件內(nèi)可見;
  • 如果你聲明為 internal,它會(huì)在相同模塊內(nèi)隨處可見;
  • protected 不適用于頂層聲明。

對模塊的理解:可見性修飾符 internal 意味著該成員只在相同模塊內(nèi)可見。更具體地說, 一個(gè)模塊是編譯在一起的一套 Kotlin 文件:

  • 一個(gè) IntelliJ IDEA 模塊;
  • 一個(gè) Maven 項(xiàng)目;
  • 一個(gè) Gradle 源集(例外是 test 源集可以訪問 main 的 internal 聲明);
  • 一次 <kotlinc> Ant 任務(wù)執(zhí)行所編譯的一套文件。

擴(kuò)展函數(shù)和擴(kuò)展屬性

聲明一個(gè)擴(kuò)展函數(shù),我們需要用一個(gè) 接收者類型 也就是被擴(kuò)展的類型來作為他的前綴。 下面代碼為 MutableList<Int> 添加一個(gè)swap 函數(shù):

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // “this”對應(yīng)該列表
    this[index1] = this[index2]
    this[index2] = tmp
}

與函數(shù)類似,Kotlin 支持?jǐn)U展屬性:

val <T> List<T>.lastIndex: Int
    get() = size - 1

注意:由于擴(kuò)展沒有實(shí)際的將成員插入類中,因此對擴(kuò)展屬性來說幕后字段是無效的。這就是為什么擴(kuò)展屬性不能有初始化器。他們的行為只能由顯式提供的 getters/setters 定義。

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

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

  • Kotlin的類和接口與Java的類和接口是有一定的區(qū)別的。Kotlin的接口是可以包含屬性聲明。Kotlin默認(rèn)...
    程自舟閱讀 10,522評論 0 11
  • 面向?qū)ο缶幊蹋∣OP) 在前面的章節(jié)中,我們學(xué)習(xí)了Kotlin的語言基礎(chǔ)知識、類型系統(tǒng)、集合類以及泛型相關(guān)的知識。...
    Tenderness4閱讀 4,622評論 1 6
  • 寫在開頭:本人打算開始寫一個(gè)Kotlin系列的教程,一是使自己記憶和理解的更加深刻,二是可以分享給同樣想學(xué)習(xí)Kot...
    胡奚冰閱讀 1,524評論 5 11
  • Kotlin的優(yōu)勢 代碼簡潔高效、強(qiáng)大的when語法,不用寫分號結(jié)尾,findViewById光榮退休,空指針安全...
    Windy_816閱讀 1,389評論 1 6
  • 我的小學(xué)班主任是教語文的,那個(gè)時(shí)候的小學(xué)和現(xiàn)在不一樣,雖然也有英語、美術(shù)、音樂等等培養(yǎng)孩子“全方面發(fā)展”的課程。但...
    江北洪閱讀 630評論 0 3

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