前言
Definitely non-nullable types
,直译为,绝对不可能为空的类型
。
Kotlin 本身就有可空和不可空类型,为何又来一个Definitely non-nullable types
呢?
背景
一起看看 Kotlin 的官方 KEEP 了解下设计初衷。
fun <T : CharSequence?> foo(t: T, tn: T?) {
t.length // call is not allowed, `t` may be null
tn.length // call is not allowed, `tn` may be null
}
fun <F : CharSequence> bar(f: F, fn: F?) {
f.length // call is allowed, `f` may not be null
fn.length // call is not allowed, `fn` may be null
}
示例 1 中,泛型类型 T
的上界是 CharSequence?
可空,所以参数 T
或者 T?
都可空;
示例 2 中,泛型类型 F
的上界是 CharSequence
不可空 ,所以参数类型 T
不可空, T?
可空。
也就是说,基于泛型类型参数的类型可能可空也可能不可空,具体取决于泛型类型边界的可空性。
泛型类型参数上界 | T | T? |
---|---|---|
可空(如 String? ) | 可空 | 可空 |
不可空(如 String ) | 不可空 | 可空 |
从上可以看出,
T?
总是能表示可空类型,T
的可空与不可空根据泛型类型边界而定。当泛型类型上界为
String
等非空类型时,我们可以用T?
表示其可空类型;当泛型类型上界为
String?
等可空类时,我们用什么表示其非空类型呢?
定义一个 Java 接口 JBox
,泛型方法 put
参数为不可空泛型类型T
。
public interface JBox {
<T> void put(@NotNull T t);
}
Kotlin中定义 JBoxImpl
实现 JBox
接口,下方是自动生成的代码。
class JBoxImpl : JBox {
override fun <T : Any?> put(t: T) {
TODO("Not yet implemented")
}
}
可以看出,IDEA 自动帮我们生成的代码并不完全正确。JBox
接口内申明的 put
方法的参数通过注解标识为非空,而 IDEA 帮我们生成的实现代码却让其参数可空。虽然不影响运行,但是 IDEA 提供了修改 Tips 。
帮开发者自动补全代码,然后提示告警,IDEA 钓鱼执法?
按照提示,我们将 T:Any?
调整为 T:Any
。
class JBoxImpl : JBox {
override fun <T : Any> put(t: T) {
println(t.toString())
}
}
但是上述代码,JBoxImpl
里的 put
方法只能接受非空类型的泛型参数,而 JBox
中仅仅只有 put
接口的参数非空而已. 无形中,我们缩小了接口的承载范围。
所以,Definitely non-nullable types
应运而生,使用 T & Any
表示。
class JBoxImpl : JBox {
override fun <T> put(t: T & Any) {
……
}
}
public interface JBox {
<T> void put(@NotNull T t);
}
对齐了,舒服了。
经典示例
fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y
fun main() {
// OK
elvisLike<String>("", "").length
// Error: 'null' cannot be a value of a non-null type
elvisLike<String>("", null).length
// OK
elvisLike<String?>(null, "").length
// Error: 'null' cannot be a value of a non-null type
elvisLike<String?>(null, null).length
}
更多推荐
有趣的 Kotlin 0x0F:Definitely non-nullable types
发布评论