• 有趣的 Kotlin 0x0F:Definitely non-nullable types


    前言

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    示例 1 中,泛型类型 T 的上界是 CharSequence?可空,所以参数 T 或者 T? 都可空;

    示例 2 中,泛型类型 F 的上界是 CharSequence不可空 ,所以参数类型 T 不可空, T? 可空。

    也就是说,基于泛型类型参数的类型可能可空也可能不可空,具体取决于泛型类型边界的可空性。

    泛型类型参数上界TT?
    可空(如 String?可空可空
    不可空(如 String不可空可空

    从上可以看出,T? 总是能表示可空类型,T 的可空与不可空根据泛型类型边界而定。

    当泛型类型上界为 String 等非空类型时,我们可以用 T? 表示其可空类型;

    当泛型类型上界为String?等可空类时,我们用什么表示其非空类型呢?

    定义一个 Java 接口 JBox,泛型方法 put 参数为不可空泛型类型T

    public interface JBox {
        <T> void put(@NotNull T t);
    }
    
    • 1
    • 2
    • 3

    Kotlin中定义 JBoxImpl 实现 JBox 接口,下方是自动生成的代码。

    class JBoxImpl : JBox {
        override fun <T : Any?> put(t: T) {
            TODO("Not yet implemented")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看出,IDEA 自动帮我们生成的代码并不完全正确。JBox 接口内申明的 put 方法的参数通过注解标识为非空,而 IDEA 帮我们生成的实现代码却让其参数可空。虽然不影响运行,但是 IDEA 提供了修改 Tips 。

    帮开发者自动补全代码,然后提示告警,IDEA 钓鱼执法?

    按照提示,我们将 T:Any? 调整为 T:Any

    class JBoxImpl : JBox {
        override fun <T : Any> put(t: T) {
            println(t.toString())
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是上述代码,JBoxImpl里的 put方法只能接受非空类型的泛型参数,而 JBox 中仅仅只有 put 接口的参数非空而已. 无形中,我们缩小了接口的承载范围。

    所以,Definitely non-nullable types 应运而生,使用 T & Any 表示。

    class JBoxImpl : JBox {
        override fun <T> put(t: T & Any) {
        	……
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public interface JBox {
        <T> void put(@NotNull T t);
    }
    
    • 1
    • 2
    • 3

    对齐了,舒服了。

    经典示例

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    Spark/Hive 行列转换
    [NCTF2019]True XML cookbook
    客户说:可视化大屏设计的不够高级,该如何破?
    Springboot文件管理 -- 实现上传下载显示删除等接口详细解析 附代码(全)
    2023_Spark_实验四:SCALA基础
    window 安装多个低版本chrome测试
    docker常见命令
    【MySQL运行原理篇】底层运行结构
    onenet MQTT之MQTT.fx通信测试 成功
    Go语句与表达式深度解析:全案例手册
  • 原文地址:https://blog.csdn.net/poorkick/article/details/126319788