聊一聊泛型的可空性(kotlin)

manerfan發表於2018-11-07

什麼?泛型本身也可以是nullable?上來就拋這麼個問題實在是不夠友好~

首先回顧,什麼是泛型?Oracle Java Tutorials

Introduced in J2SE 5.0, this long-awaited enhancement to the type system allows a type or method to operate on objects of various types while providing compile-time type safety. It adds compile-time type safety to the Collections Framework and eliminates the drudgery of casting.

泛型的本質是引數化型別,也就是說操作的資料型別被指定為一個引數。簡單來講,泛型就是操作型別的佔位符。

那,為什麼要使用泛型?Oracle Java Tutorials

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types.

泛型的好處是在編譯的時候做型別安全檢查,並且所有的強制轉換都是自動和隱式的,提高程式碼的重用率。

在深入討論泛型的可空性之前,首先看一個簡單的例子 (kotlin中更多泛型的介紹,以及其與java泛型的區別,可以檢視Generics)

    fun <T> whoAmI(t: T) {
        val clazz = t::class
        println("I`m $clazz")
    }

上面的程式碼是編譯不過的,問題在於

image-20181106093104446.png

nullable type `T`是什麼鬼?OK,我們按照提示,為引數t加上!!強制標識為not-null (關於!!的使用,可以檢視 Null Safety)

image-20181106093259249.png

WTF~ 嗶~ 卒~

在繼續討論之前,先關注兩個概念,type parametertype argument,在一個函式中,前者是函式引數的型別(型別),如translate(java.lang.String, java.lang.String)中的java.lang.String,而後者則是函式呼叫時傳入的具體值(型別),如translate("Hello You!", "zh-cn")中的Hello Youzh-cn,泛型作為一個佔位符,佔的正是type parameter的位

首先簡單瞭解一下argument的可空性,kotlin中控制函式引數值(類屬性值等)的nullable是通過?符號實現的,預設均為non-nullable,如以下程式碼片段是編譯不過的

image-20181107090844996.png

只有指定引數namenullable,才可以順利編譯

image-20181107091014490.png

同樣,對應泛型類,我們也可以指定nullablenon-nullabletype argument

image-20181107092315176.png

從上例中可以看到,null在kotlin中的型別為Nothing?(kotlin中一切皆為物件)

更多內容請參考 Null Safety

說了這麼多,到底什麼是泛型的可空性(type argumentnullable)?

上例中,之所以可以將forestA指定為Forest<Tree?>,是因為,每一個nullabletype argument都有一個隱式邊界Any?,即如下兩種類宣告完全等價

    // T的隱式邊界為Any?
    class Forest<T>
    
    // 顯示指定T的邊界為Any?
    class Forest<T : Any?>
    
    // 顯示指定T的邊界為Tree?
    class Forest<T : Tree?>

如果將Forest中的泛型強制指定為non-nullable會發生什麼

image-20181107094503992.png

什麼?不能將nullableTree?應用到Forest類了!這是因為Forest類的定義中,我們強制將泛型的邊界指定為了non-nullableTree

所以,回到文章的開頭,函式whoAmI的錯誤資訊nullable type `T`指的是哪個鬼?泛型T的預設邊界為Any?,即T : Any?,所以,我們只需要顯示指定Tnon-nullable即可

image-20181107101306195.png

至此,對於nullablenon-nullabletype parametertype argument是否有所瞭解?


相關文章