在之前的三篇文章中直秆,我們已經(jīng)了解了一下的內(nèi)容:
- 如何在項(xiàng)目中使用 Kotlin濒募,Kotlin 的空安全
- 集合的相關(guān)操作,擴(kuò)展函數(shù)以及 Lambda 表達(dá)式等高級(jí)特性
- for 與集合遍歷圾结,強(qiáng)大的 if 瑰剃、when 表達(dá)式,可以用來做單例模式的伴生對(duì)象
1筝野、數(shù)據(jù)類
你是否已經(jīng)厭煩了一個(gè)項(xiàng)目中大量的 POJO 晌姚?是的,雖然我們有大量的插件來幫助我們簡(jiǎn)化這些創(chuàng)建過程歇竟,但是滿天的 getter
與 setter
還有那些 equals()/hashCode()
大量機(jī)械的挥唠、重復(fù)的方法,早已讓我們厭煩不堪途蒋。
Kotlin 為我們提供了更強(qiáng)大數(shù)據(jù)類猛遍,如下所示:
data class User(var name: String="", var age:Int = 0){
var nickname:String = ""
}
使用 data 關(guān)鍵字來告知系統(tǒng)這是一個(gè)數(shù)據(jù)類,其主構(gòu)造函數(shù)需要至少有一個(gè)參數(shù)号坡,如果生成的類需要含有一個(gè)無(wú)參的構(gòu)造函數(shù)懊烤,則所有的屬性必須指定默認(rèn)值。
請(qǐng)注意宽堆,對(duì)于那些自動(dòng)生成的函數(shù)腌紧,編譯器只使用在主構(gòu)造函數(shù)內(nèi)部定義的屬性。如需在生成的實(shí)現(xiàn)中排出一個(gè)屬性畜隶,請(qǐng)將其聲明在類體中壁肋。
1.1、復(fù)制一個(gè)數(shù)據(jù)類
當(dāng)我們使用 copy
函數(shù)時(shí)籽慢,系統(tǒng)會(huì)找到該類的主構(gòu)造函數(shù)的簽名浸遗,此時(shí)我們可以按順序填入?yún)?shù)(無(wú)需完全填寫,會(huì)從第一位開始自動(dòng)匹配)箱亿,還可以采用類似 Python 的方式 參數(shù)名 = 值
的方式來修改我們需要的參數(shù)值跛锌。
2、解構(gòu)聲明
這四個(gè)字對(duì)于 Android + Java 開發(fā)者是挺陌生的届惋,大家可以點(diǎn)擊到 Kotlin 中文站查看詳細(xì)的定義髓帽,下面我們通過代碼來了解一下他的用法,首先我們來看一張熟悉的圖:
注意這里的 (k,v)
這就是一個(gè)解構(gòu)聲明脑豹,標(biāo)準(zhǔn)庫(kù)已經(jīng)幫我們實(shí)現(xiàn)了解構(gòu)聲明郑藏,另外數(shù)據(jù)類也已經(jīng)實(shí)現(xiàn)了主構(gòu)造函數(shù)包含參數(shù)的解構(gòu)聲明。
比如剛剛我們創(chuàng)建的 User 類瘩欺,我們可以通過如下的方式來獲取其中指定的值:
var (name,age) = c
var (_,aged) = d //用下劃線占位不需要的參數(shù)
var aged1 = d.component2() //直接使用 componentN 函數(shù)來獲取對(duì)應(yīng)的值
println("c's name = $name c's age = $age")
println("don't need d's name, d's age = $aged")
println("other way to get d's age is $aged1")
我們可以任意的在類中進(jìn)行解構(gòu)聲明必盖,比如我們可以使用解構(gòu)聲明來擴(kuò)展剛剛我們定義的 User 類:
data class User(var name: String="", var age:Int = 0){
var nickname:String = ""
operator fun component3() = nickname
}
需要注意的是拌牲,數(shù)據(jù)類默認(rèn)為我們實(shí)現(xiàn)了主構(gòu)造函數(shù)的參數(shù)解構(gòu)聲明,所以我們的解構(gòu)聲明只能接著主構(gòu)造函數(shù)的序號(hào)往后使用
2.1筑悴、在 Lambda 表達(dá)式中使用解構(gòu)聲明
當(dāng)這個(gè)類進(jìn)行了解構(gòu)聲明時(shí)们拙,我們可以直接在 Lambda 表達(dá)式中使用其解構(gòu)稍途,如下:
c.let { (a,b,c) ->
println("c's name = $a , c's age = $b , c's nickname = $c")
}
請(qǐng)注意解構(gòu)聲明對(duì)于 Lambda 表達(dá)式而言是一個(gè)參數(shù):
{ a //-> …… } // 一個(gè)參數(shù)
{ a, b //-> …… } // 兩個(gè)參數(shù)
{ (a, b) //-> …… } // 一個(gè)解構(gòu)對(duì)
{ (a, b), c //-> …… } // 一個(gè)解構(gòu)對(duì)以及其他參數(shù)
在函數(shù)中返回?cái)?shù)個(gè)參數(shù)
有的時(shí)候我們經(jīng)常會(huì)希望一個(gè)函數(shù)能返給我們多個(gè)結(jié)果阁吝,例如曾經(jīng)我做過一個(gè)小功能:傳入 PM2.5 的值,返回當(dāng)前 PM2.5 嚴(yán)重程度的介紹以及需要顯示的顏色代碼械拍。當(dāng)時(shí)我采用的做法是返回一個(gè) Map突勇,然后再需要的地方在使用固定的 key 將值提取出來,這一點(diǎn)也不優(yōu)雅坷虑。
但是在 Kotlin 里 你可以這樣做:
fun getPM25DescAndColor(pm25: Int):Pair<String,String> {
return when (pm25) {
in 0..50 -> Pair("優(yōu)秀","#009900")
in 51..100 -> Pair("良好","#FFCC33")
in 101..150 -> Pair("輕度","#FF9900")
in 151..200 -> Pair("中度","#FF3300")
in 201..300 -> Pair("重度","#9900FF")
else -> Pair("嚴(yán)重","#663300")
}
}
標(biāo)準(zhǔn)庫(kù)提供了 Pair
和 Triple
這兩個(gè)類甲馋,前者表示兩個(gè)參數(shù),后者表示三個(gè)參數(shù)迄损。盡管在很多情況下命名數(shù)據(jù)類是更好的設(shè)計(jì)選擇定躏, 因?yàn)樗鼈兺ㄟ^為屬性提供有意義的名稱使代碼更具可讀性。
3芹敌、單例模式
不要在考慮單例模式的幾種寫法了痊远,也不要在關(guān)注什么線程安全、線程不安全了氏捞。
不知道你有沒有疑惑過碧聪,Java 是一個(gè)面向?qū)ο缶幊痰恼Z(yǔ)言(OOP),但是 Java 里有些東西似乎不那么 OOP液茎,比如單例模式逞姿,又比如公共靜態(tài)方法。這兩者都必須在一個(gè) class 下面捆等,他們卻都不應(yīng)該是一個(gè)類滞造。一個(gè)單例模式,既然是一個(gè) class 為什么不能處處實(shí)例化它栋烤?一個(gè)個(gè)公共靜態(tài)方法谒养,調(diào)用它為什么還要先寫一個(gè) class?這并不符合直覺班缎!
Kotlin 幫我們徹底的解決了這一迷思蝴光!本篇我們先來說說在 Kotlin 里如何寫好一個(gè)單例模式。
在 Java 里我最常用的單例模式是靜態(tài)內(nèi)部類达址,因?yàn)樗词蔷€程安全又是懶加載的蔑祟,我們來看一下一個(gè)最簡(jiǎn)單的靜態(tài)內(nèi)部類單例模式:
public class Singleton {
//構(gòu)造方法私有
private Singleton() {
}
//在訪問HttpMethods時(shí)創(chuàng)建單例
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
//獲取單例
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
我們通過私有的構(gòu)造函數(shù)來使得外部類不能創(chuàng)建這個(gè)類的實(shí)例,在私有的內(nèi)部靜態(tài)類中實(shí)例化這個(gè)類沉唠,然后提供一個(gè)公共靜態(tài)訪問內(nèi)部類中的成員疆虚。
我們來看看這樣一個(gè)單例我們?cè)?Kotlin 中應(yīng)該如何處理:
object Singleton {
fun doSomething(){
}
}
是的就是這么簡(jiǎn)單,我們只要使用 object
關(guān)鍵字就可以聲明一個(gè)單例,這是真正意義上的單例径簿,因?yàn)樗旧砭褪且粋€(gè)對(duì)象罢屈!在 Kotlin 中這被稱作對(duì)象聲明,就像變量聲明一樣篇亭,對(duì)象聲明不是一個(gè)表達(dá)式缠捌,不能用在賦值語(yǔ)句的右邊。
對(duì)象聲明的初始化過程是線程安全的译蒂。
如需引用該對(duì)象曼月,我們直接使用其名稱即可:
Singleton.doSomething()
如果你只是在某個(gè) kt 文件中需要使用一個(gè)單例模式的對(duì)象,我們還可以使用 object 關(guān)鍵字來實(shí)現(xiàn) Java 中的匿名內(nèi)部類:
var e = object {
fun print(index: Int): String {
return "你輸入的數(shù)字是:$index"
}
}
println(e.print(34))
當(dāng)然這個(gè)對(duì)象還可存在繼承與實(shí)現(xiàn):
open class A(x: Int) {
public open val y: Int = x
}
interface B {……}
val ab: A = object : A(1), B {
override val y = 15
}
PS:誠(chéng)如本篇文章的標(biāo)題柔昼,簡(jiǎn)單就是美哑芹,Kotlin 中還有很多這樣充滿美感的設(shè)計(jì)或是語(yǔ)法糖,請(qǐng)繼續(xù)關(guān)注后續(xù)文章捕透!