Kotlin 學(xué)習(xí)筆記(五)類型、密封類叠穆、數(shù)據(jù)協(xié)變少漆、類型投射和泛型

PipingPlover_ZH-CN0992806167_1920x1080.jpg

前言
本文章只是用于記錄學(xué)習(xí),所以部分地方如果有錯誤或者理解不對的地方硼被,麻煩請指正示损。本篇為 csdn 原文章 轉(zhuǎn)移修改版 原文章

Kotlin 學(xué)習(xí)筆記(四)

簡述:

  1. kotlin 中數(shù)據(jù)類的聲明及條件
  2. kotlin 中 密封類/ 枚舉
  3. kotlin 中類型判斷
  4. kotlin 中泛型 和 數(shù)據(jù)協(xié)變
  5. kotlin 中的類型投射

1. 數(shù)據(jù)類

??在java 中我們通常會創(chuàng)建很多 Bean 類來存儲 數(shù)據(jù),在kotlin 中有專門的數(shù)據(jù)類嚷硫,“data”

data class User(val name: String, val age: Int)

數(shù)據(jù)類必須滿足幾個條件

  1. 主構(gòu)造函數(shù)需要至少有一個參數(shù)检访;
  2. 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 val 或 var始鱼;
  3. 數(shù)據(jù)類不能是抽象、開放脆贵、密封或者內(nèi)部的医清;

數(shù)據(jù)類 也為我們自動生成了部分代碼:

  1. equals()/hashCode() 對;
  2. toString() 格式是 "User(name=John, age=42)"丹禀;
  3. componentN() 函數(shù) 按聲明順序?qū)?yīng)于所有屬性状勤;
  4. copy() 函數(shù)

??在 JVM 中,如果生成的類需要含有一個無參的構(gòu)造函數(shù)双泪,則所有的屬性必須指定默認(rèn)值持搜。

data class User(val name: String = "", val age: Int = 0)

??下面我們使用代碼簡單聯(lián)系一下

data class User(val name: String, val age: Int)


fun main(args: Array<String>) {
    var json = User(name = "ymc",age = 1)
    val json1 = json.copy(age = 2)
    println(json1)  // 默認(rèn)調(diào)用 User的 tostring()
}

?? 在java 中我們通常想要賦值一個值,但是只需要改變某一項的值的數(shù)據(jù)信息焙矛,kotlin 中的 copy函數(shù)葫盼,只需要傳入不一樣的數(shù)據(jù),就會自動化改變村斟,并返回給你修改后的所有數(shù)據(jù)信息贫导。

2.密封類

??密封類,可以理解為枚舉蟆盹,規(guī)定了有限個類型孩灯,不可以存在其他類型,但枚舉每個枚舉常量只存在一個示例逾滥,但是密封類的子類可以有多個示例峰档,所以可以將密封類看做是枚舉的拓展,基于枚舉寨昙,高于枚舉讥巡,青出于藍(lán)而勝于藍(lán)。

??聲明一個密封類舔哪,需要在類名前面添加 sealed 修飾符欢顷。雖然密封類也可以有子類,但是所有子類都必須在與密封類自身相同的文件中聲明捉蚤。eg:

// 密封類
sealed class Expr

data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

??相對于密封類的子類 必須要在一個文件中抬驴,擴展密封類子類的類(間接繼承者)可以放在任何位置,而無需在同一個文件中缆巧。

密封類注意點:

1.一個密封類是自身抽象的怎爵,它不能直接實例化 ,但是可以有抽象(abstract)成員盅蝗。
2.密封類不允許有非-private 構(gòu)造函數(shù)(其構(gòu)造函數(shù)默認(rèn)為 private)。

??使用密封類的關(guān)鍵好處在于使用 when 表達式 的時候姆蘸,如果能夠驗證語句覆蓋了所有情況墩莫,就不需要為該語句再添加一個 else 子句了芙委。

fun eval(expr: Expr): Double{
    return when(expr) {
        is Const -> expr.number
        is Sum -> eval(expr.e1) + eval(expr.e2)
        NotANumber -> Double.NaN
    }
}

2. 類型檢測以及自動類型轉(zhuǎn)換

我們可以使用 is 運算符檢測一個表達式是否某類型的一個實例(類似于Java中的instanceof關(guān)鍵字)。

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // 做過類型判斷以后狂秦,obj會被系統(tǒng)自動轉(zhuǎn)換為String類型
        return obj.length
    }

    //在這里還有一種方法灌侣,與Java中instanceof不同,使用!is
    // if (obj !is String){
    //   // XXX
    // }

    // 這里的obj仍然是Any類型的引用
    return null
}

或者 可以再運算式中 使用

fun getStringLength(obj: Any): Int? {
  // 在 `&&` 運算符的右側(cè), `obj` 的類型會被自動轉(zhuǎn)換為 `String`
  if (obj is String && obj.length > 0)
    return obj.length
  return null
}

3. 泛型

??kotlin中的泛型和 java 中的差不多裂问,但是很多方面更加簡潔侧啼。

// kotlin中的 泛型
class Box<T>(t: T) {
    var value = t
}

如果我們要創(chuàng)建

val box: Box<Int> = Box<Int>(1)
// 1 具有類型 Int,所以編譯器知道我們說的是 Box<Int>堪簿。
val box = Box(1) 

在學(xué)習(xí)下邊Kotlin 的 泛型特性的時候痊乾,我們先回顧一下 java 中的 泛型 使用

  1. 通配符上界,只能從中讀取元素椭更,不能添加元素哪审,稱為生產(chǎn)者(Producers),用< ? extends T>表示虑瀑。
  2. 通配符下界湿滓,只能添加元素,不能直接讀取下界類型的元素舌狗,稱為消費者(Consumers)叽奥,用< ? super T>表示。

3.1 通配符上界

??< ? extends T>(T表示通配符的上界)痛侍,表示可以接收T以及T的子類參數(shù)朝氓,也就是說可以安全的讀取到T的實例,事實上所有的集合元素都是T的子類的實例恋日,但不能向其添加元素膀篮,因為沒法確定添加的實例類型跟定義的類型是否匹配

List<String> strs = new ArrayList<String>();
strs.add("0");
strs.add("1");
List<? extends Object> objs = strs;

objs.get(0); // 可以獲取

objs.add(1); // 但是添加的時候報錯

??經(jīng)過本人測試,不管添加 Int 岂膳,String 類型都提示無法添加誓竿,上面的例子說明了objs可以讀取值,但是再往objs里面添加值的時候谈截,就會出錯筷屡,沒法確定添加的實例類型跟定義的類型是否匹配。

3.2 通配符下界

< ? super T>簸喂,其中T就表示通配符的下界毙死。
舉個栗子:Collection< ? super String>是Collection< String>的父類型,所以可以直接add和set喻鳄,但是get的時候獲取到的類型是Object而不是String類型扼倘。

List<String> strs = new ArrayList<String>();
strs.add("0");
List<? super String> objs = strs;
objs.add("1");
objs.set(0, "2");
// 得到Object類型,如果想要String 還需要強轉(zhuǎn)
Object s = objs.get(0);

Kotlin 中的泛型

??不管是Java還是Kotlin,泛型都是使用擦除來實現(xiàn)的再菊,這意味著當(dāng)你在使用泛型時爪喘,任務(wù)具體的類型信息都被擦除的,你唯一知道的就是你再使用一個對象纠拔。比如秉剑,Box<String>和Box<Int>在運行時是想的類型,都是Box的實例稠诲。在使用泛型時侦鹏,具體類型信息的擦除是我們不不懂得不面對的,在Kotlin中也為我們提供了一些可供參考的解決方案:臀叙、

  1. 類型協(xié)變
  2. 類型投射
  3. 泛型約束

3.3 類型協(xié)變

??假設(shè)我們有一個泛型接口Source< in T, out R >, 其中T由協(xié)變注解in修飾略水,R由協(xié)變注解Out修飾.

internal interface Source<in T, out R> {

    // in 函數(shù),可以當(dāng)做參數(shù)使用匹耕,消費聚请,但是不能作為返回值
    fun mapT(t: T): Unit
    
    // out 函數(shù),不能用來當(dāng)參數(shù)稳其,不能消費驶赏,但是可以作為返回值
    fun nextR(): R
}

in T: ? ? 來確保Source的成員函數(shù)只能消費T類型,而不能返回T類型
out R:? 來確保Source的成員函數(shù)只能返回R類型既鞠,而不能消費R類型

??從上面的解釋中煤傍,我們可以清楚的知道了協(xié)變注解in和out的用意,其實際上是定義了類型參數(shù)在該類或者接口的用途嘱蛋,是用來消費的還是用來返回的蚯姆,對其做了相應(yīng)的限定。

3.4 類型投射

??從上述代碼中我們了解到 泛型的 in 和 out 的使用洒敏,下面我們通過一段 代碼了解 到底什么是 類型投射龄恋。

fun copy(from: Array<out String>, to: Array<Any>) {
    // ...
}

fun fill(dest: Array<in String>, value: String) {
    // ...
}

??from的泛型參數(shù)使用了協(xié)變注解out修飾,意味著該參數(shù)不能在該函數(shù)中消費凶伙,在該方法中 禁止 對該參數(shù)進行任何操作郭毕。

??對于fill函數(shù)中,dest的泛型參數(shù)使用了協(xié)變注解in修飾函荣,Array<in String>與Java的 Array < ? super String> 相同, 也就是說, 你可以使用CharSequence數(shù)組,或者 Object 數(shù)組作為 fill() 函數(shù)的參數(shù)显押、

??這種聲明在Kotlin中稱為類型投射(type projection),類型投射的主要用于對參數(shù)做了相對因的限定傻挂,避免了對該參數(shù)類的不安全操作乘碑。

3.5 泛型函數(shù)

??類可以有類型參數(shù)。函數(shù)也可以有金拒。類型參數(shù)要放在函數(shù)名稱之前:

fun <T> singletonList(item: T): List<T> {
    // ……
}

fun <T> T.basicToString() : String {  // 擴展函數(shù)
    // ……
}

// 調(diào)用方式 指定 類型為 int
val l = singletonList<Int>(1)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兽肤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌轿衔,老刑警劉巖沉迹,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異害驹,居然都是意外死亡,警方通過查閱死者的電腦和手機蛤育,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門宛官,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓦糕,你說我怎么就攤上這事底洗。” “怎么了咕娄?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵亥揖,是天一觀的道長。 經(jīng)常有香客問我圣勒,道長费变,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任圣贸,我火速辦了婚禮挚歧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吁峻。我一直安慰自己滑负,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布用含。 她就那樣靜靜地躺著矮慕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪啄骇。 梳的紋絲不亂的頭發(fā)上痴鳄,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音肠缔,去河邊找鬼夏跷。 笑死,一個胖子當(dāng)著我的面吹牛明未,可吹牛的內(nèi)容都是我干的槽华。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼趟妥,長吁一口氣:“原來是場噩夢啊……” “哼猫态!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤亲雪,失蹤者是張志新(化名)和其女友劉穎勇凭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體义辕,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡虾标,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了灌砖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片璧函。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖基显,靈堂內(nèi)的尸體忽然破棺而出蘸吓,到底是詐尸還是另有隱情,我是刑警寧澤撩幽,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布库继,位于F島的核電站,受9級特大地震影響窜醉,放射性物質(zhì)發(fā)生泄漏宪萄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一酱虎、第九天 我趴在偏房一處隱蔽的房頂上張望雨膨。 院中可真熱鬧,春花似錦读串、人聲如沸聊记。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽排监。三九已至,卻和暖如春杰捂,著一層夾襖步出監(jiān)牢的瞬間舆床,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工嫁佳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挨队,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓蒿往,卻偏偏與公主長得像盛垦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瓤漏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345