介紹Kotlin第二部分(翻譯篇)

前言

Kotlin介紹:第一部分,我們介紹了基本語法,現(xiàn)在我們可以去看看實(shí)際上如何使用Kotlin。在這篇文章中,我們將介紹collectionslambdas表達(dá)式,一些方便的擴(kuò)展函數(shù)(apply,letrunwith),null safety(空安全),那下面咱就開始吧。

1、Collections and Lambdas

那么Kotlin collections是什么呢?如果您熟悉Java8,您將會對這些collection方法(java流)和語法十分了解。然而,Kotlin提供了大部分你可能想得到的擴(kuò)展,讓我們一起來看看吧。

listOf(1,2,3)
mutableListOf("a", "b", "c")
 
setOf(1,2,3)
mutableSetOf("a", "b", "c")
 
mapOf(1 to "a", 2 to "b", 3 to "c")
mutableMapOf("a" to 1, "b" to 2, "c" to 3)

這些是基礎(chǔ),Kotlin為您提供了方法來創(chuàng)建collections,我在這兒列出了不可變和可變版本的List,Set和Map。Kotlin系列的編程除了默認(rèn)的不變性外,還來自于Kotlin stdlib的擴(kuò)展功能。如果您熟悉函數(shù)式編程,那么您將熟悉大部分功能。它們是一組輔助函數(shù)和更高級的輔助函數(shù),可以為您的集合提供常用操作。有了這些擴(kuò)展函數(shù)(map,flatMap,forEachfoldreduce,filter,zip,...)很多操作完成起來就很方便。
在我們使用它們之前,我們需要先說一下lambdas表達(dá)式。Kotlin標(biāo)準(zhǔn)庫的collection擴(kuò)展功能的優(yōu)點(diǎn)來自于易使用lambdas表達(dá)式,只需使用足夠的類型推理來保證編程安全。在Kotlin中有幾種方法來定義lambdas函數(shù)。

val aList = listOf(1,2,4)
aList.map { elem ->
    elem + 1
} // 2,3,5
 
aList.filter { it != 1} // 2,4
 
fun folder(a: Int, b: Int) = a + b
aList.reduce(::folder) // 7
// 或者: aList.reduce { a, b -> folder(a, b) }

在第一個例子中,我們定義了Kotlin lambdas的最常見用法。我們可以用角括號(->)來縮寫匿名函數(shù),我們可以改變lambdas參數(shù)的名稱(在這里我們省略了類型定義;我們可以從aList列表中看到它是一個Int),然后我們定義lambda體,不需要使用return語句,最后一行將被返回。

下一個例子進(jìn)一步說明,甚至可以省略參數(shù)定義。在Kotlin中,默認(rèn)情況下,一個參數(shù)lambdas會接收到一個名為it的參數(shù)名。沒有必要去命名它。請注意,如果過多的使用it,尤其在嵌套函數(shù)中,會導(dǎo)致代碼非?;靵y!

最后一個向我們展示了幾個新的概念,首先是一個本地函數(shù),我們引用了::一個雙匯語法,本地函數(shù)的樣式和作用類似于類或全局作用域函數(shù),但還有一個額外功能,它還能訪問與函數(shù)本身在同一范圍定義的變量。引用本地函數(shù)的第二種方法我們將它稱為內(nèi)部lambda,就像注釋中顯示的那樣。

正如你所看到的,Kotlin中的lambdas是以直截了當(dāng)?shù)姆绞蕉x的。它們在您的代碼中也很明顯,并使得高階函數(shù)的使用變得簡單。關(guān)于Kotlin和lambdas的最好部分是類型推斷,當(dāng)類型不匹配時,它就在你的代碼下面出現(xiàn)一條紅色的線。通過編譯器的這種幫助,您可以將精力放在業(yè)務(wù)邏輯上,而不是試圖找出循環(huán)應(yīng)該遍歷多少遍。

有關(guān)Kotlin的collection擴(kuò)展功能的更多信息可以在官方網(wǎng)站API doc中找到

2、Null safety(空安全)

當(dāng)涉及到可空性,Kotlin編譯器會非常嚴(yán)格的剖析您的代碼。如果定義一個可能為null的變量,則需要將其定義為可空。那這該怎么寫呢?

var nil: String? = null
val notNil: String = "Hi"
var nil = null

這三個變量聲明有兩個可空值,一個不為null。無效性的共同點(diǎn)是問號;可空變量和函數(shù)參數(shù)用問號定義。這個問號在Kotlin的null safe起著重要的作用。如果Kotlin編譯器在變量聲明或函數(shù)參數(shù)/返回類型中看到這個問號,它將強(qiáng)制您對空檢查。如果您主要編寫的是Kotlin代碼,那您將會從NullPointException解放出來。然而Kotlin與Java高度互操作,當(dāng)你傳入的數(shù)據(jù)可能為空時。Kotlin會讓你處理這個十億美元的錯誤。

data class Lad(val name: String, val age: Int)
fun doSomething(laddy: Lad?){
    print(laddy.name)
}

如果您嘗試這么做,Kotlin會編譯器將會給出提示。在android studio中,您將得到文本下方的紅色波浪線,它會給出Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Lad?。為了解決這個問題,你別無選擇。

fun doSomething(laddy: Lad?){
    if(laddy != null){
        print(laddy.name)
    }
}
 
fun doSomething(laddy: Lad?){
    print(laddy?.name)
}
 
fun doSomething(laddy: Lad?){
    laddy?.name?.let {
        print(it)
    }
    /** 或者
    * laddy?.name?.let { name ->
    *     print(name)
    * }
    **/
}

第一個例子是之前的寫法,正確的非空檢查。編譯器知道,在完成null檢查之后,就可以使用我們的變量,紅色波浪線就會從print語句中消失。在第二個例子,我們熟悉的問號再次出現(xiàn)了,但是這一次擔(dān)任是不同的角色。在這方面,問號會提示If laddy is not null, then take the name property from it。如果laddy為空,那么null將會打印到控制臺。

第三個介紹了一個擴(kuò)展功能,我們可以用它來調(diào)用let。如果我們想從我們的函數(shù)返回一些東西,我們可以使用elvis作為默認(rèn)值,以防我們碰到一個null。使用elvis有點(diǎn)像這樣:

fun doSomething(laddy: Lad?) = laddy?.name?: "James"

當(dāng)laddy和name都不為空時,才會返回“James”。

3、擴(kuò)展功能(Apply, Let, Run, and With)

Kotlin推出了一些擴(kuò)展功能,可以幫助我們處理。我們看到的第一個let是一個擴(kuò)展,它將一個lambda作為參數(shù)。在上面的例子中,it意味著我們的對象屬性name,但僅當(dāng)laddy和name不為空時有效。let只對存在的東西有用,作為擴(kuò)展功能,它不能擴(kuò)展不存在的東西。

Apply是另一個時髦的擴(kuò)展功能,我們可以在很多情況下使用它,一個常見的用法的就是創(chuàng)建一個需要許多調(diào)用的對象,但是沒有很好的方法來做到這一點(diǎn)。為了簡單起見,我們能想到JavaBean及其getter和seeter。

public class JavaBeanClass {
   private String thing;
   private String thang;
 
   public String getThing() {           
       return thing;                    
   }                                    
                                        
   public void setThing(String thing) { 
       this.thing = thing;              
   }                                    
                                        
   public String getThang() {           
       return thang;                    
   }                                    
                                        
   public void setThang(String thang) { 
       this.thang = thang;              
   }                                    
}

這看起來有點(diǎn)繁瑣,沒關(guān)系,讓我們使用Kotlin看看。

val mrBean = JavaBeanClass().apply {
    setThing("Wild")
    setThang("erbeest")
}

這就很舒服了,其實(shí)在Kotlin中,還可以有其它的寫法,與上述相同的代碼還可以這么寫:

val mrBean = JavaBeanClass().apply {
    thing = "Wild"
    thang = "erbeest"
}

這樣就更簡潔了。

接下來我們介紹with,這個家伙類似apply,實(shí)際上它不是一個擴(kuò)展函數(shù),它只是一個函數(shù),接受了兩個參數(shù)。我們來看一個例子,我們將使用與mrBean之前定義的相同的方法。

with(mrBean) {
    thing = "the"
    thang = "ain't no"
}

和apply非常相似,你不覺得嗎?其實(shí)根本不一樣,那是因?yàn)槲覀儧]有做任何事,with返回with塊中最后一個表達(dá)式的值。這是一個重要的區(qū)別,所以讓我們看一個更好的例子。

val yo = with(mrBean) {
    thang + "thing"
}
print(yo) // ain't nothing

我們繼續(xù)看下一個操作符run,這是一個很簡單的小東西。它是一個擴(kuò)展函數(shù),它接受一個參數(shù),一個lambda。它只是調(diào)用該lambda并返回該lambda的響應(yīng)。“那么這個家伙有什么用呢?” “你可能會問”。使用它來運(yùn)行某些東西,當(dāng)且僅當(dāng)它被調(diào)用的對象不是null(使用它類似于let上面的幾行,但在run這種情況下this作為范圍的對象)或使用它來調(diào)用我們的函數(shù)調(diào)用并保護(hù)我們的lambdas。我們必須記住,做run同樣的事情,但with通常更容易使用。

4、類型: Checking, casting, and safety(檢查,轉(zhuǎn)換,安全)

在Java世界中,您可能會遇到這樣的if檢查if (clazz instanceOf SomeClass)程序員希望看到他們是否正確實(shí)現(xiàn)其接口或擴(kuò)展的基類。
在Kotlin中類型推斷是非常好的,編譯器在編寫代碼時給出了很多有用的提示。當(dāng)您需要檢查對象是否是某種類型時,您可以使用is關(guān)鍵字。

fun tryAndFailToCompileToGetTheAnswer(plzPassInThirteen: Any): Int {
    return plzPassInThirteen + 29
}
 
fun getTheAnswer(plzPassInThirteen: Any): Int {
    if (plzPassInThirteen is Int) {
        return plzPassInThirteen + 29
    }
    return 666
}
println(getTheAnswer(13)) // 42

在上面的代碼塊中,第一個函數(shù)將會失敗,并且根本沒有實(shí)際編譯,它會報錯,找不到類型匹配。第二個功能修復(fù)了:它做了一個簡單的is檢查,在這一點(diǎn)上,Kotlin智能的將該值轉(zhuǎn)換為Int,因此它可以在if語句中使用。通常當(dāng)when和is配合使用時,您可以這么寫:

fun getTheAnswer(plzPassInThirteen: Any): Int = when(plzPassInThirteen) {
    is Int -> plzPassInThirteen + 29
    else -> 666
}
println(getTheAnswer(13)) // 42

這個例子與以前看到的if語句是一樣的,但這不是更美觀嗎?

現(xiàn)在我們接觸了iswhen在一起,現(xiàn)在我們可以繞個彎子談一談sealed classes,Kotlin有一個sealed classes的概念,我們可以把它當(dāng)成一些子類的包裝。

sealed class Seal
class SeaLion: Seal()
class Walrus: Seal()
class KissFromARose(val film: String): Seal()

如果我們有這樣的結(jié)構(gòu),一個密封的超類和三個繼承的子類,我們可以很好的處理多態(tài)和when以及is的組合。

fun getTheAnswer(songOrAnimal: Seal): Unit = when(songOrAnimal) {
    is SeaLion -> println("Animal")
    is Walrus -> println("Song by Beatles")
}

這是編譯不過去的,編譯器會告訴我們when中的聲明少了哪一個子類,如果我們將KissFromARose添加上就不會出現(xiàn)問題。

fun getTheAnswer(songOrAnimal: Seal): Unit = when(songOrAnimal) {
    is SeaLion -> println("Animal")
    is Walrus -> println("Song by Beatles")
    is KissFromARose -> ("Heidi Klum")
}
println(getTheAnswer(Walrus())) // Song by Beatles

上面的編譯就沒什么問題,

有時候我們需要類型的轉(zhuǎn)換,在Kotlin中,使用as關(guān)鍵字。當(dāng)它被賦值時,我們可以假設(shè)它被轉(zhuǎn)換為該類型,

val possiblyString: Any = "definitely"
possiblyString.capitalize()

上面的例子是無法編譯的,capitalize()會有錯誤下劃線,編譯器告訴我們有一個Unresolved reference和resolver type mismatch。這個提示是對的,我們知道Any沒有capitalize()方法,修改這個是容易的,我們只要將變量變成String就沒問題了。

val possiblyString: Any = "definitely"
possiblyString as String
possiblyString.capitalize()

現(xiàn)在我們已經(jīng)了解了Kotlin的集合空安全,類型安全,到這里第二部分的內(nèi)容也算是告一段落了。

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

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

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