Kotlin 与 Java 如何解决 Null 问题?

【CSDN 编者按】Kotlin 和 Java 是如何解决 Null 问题?本文作者分享了解决思路。

原文链接:https://blog.frankel.ch/null-safety-java-vs-kotlin/

未经授权,禁止转载!


作者 | Nicolas Fränkel   责编 | 弯月出品 | CSDN(ID:CSDNnews)

在本文中,我想讨论一下 Kotlin 和 Java 是如何解决 Null 问题的。


可为 Null


相信每一位从事软件开发超过两年的人都听过下面这句话:

我把 Null 引用称为自己的十亿美元错误。它的发明是在 1965 年,那时我用一个面向对象语言(ALGOL W)设计了第一个全面的引用类型系统。我的目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。但是我未能抵御住诱惑,加入了 Null 引用,仅仅是因为实现起来非常容易。结果导致了数不清的错误、漏洞和系统崩溃,可能在之后的 40 年中造成了十亿美元的损失。

                          —— 图灵奖得主 Tony Hoare

Null 背后的基本思想是能够定义一个未初始化的变量。当有人调用这类变量的某个成员时,运行时就会寻找变量的内存地址,结果就是引用失败,因为其后面没有任何东西。

许多编程语言都包含 Null 值,只不过名称不同罢了:

  • Python 有 None;

  • JavaScript 有 null;

  • Java、Scala 和 Kotlin 也有 null;

  • Ruby 有 nil;

  • 以及其他等等。

有些不允许使用未初始化的值,比如 Rust。


Kotlin 中的 Null 安全性


如上所示,Kotlin 也有 null 值。只不过,null 融入到了类型系统中。在 Kotlin 中,每个类型 X 实际上都有两种类型:

X:不可为 null,类型 X 的任何变量都不可以为 null。编译器会确保这一点;

val str: String = null

上述代码无法通过编译。

X?:可以为 null。

val str: String? = null

上述代码可以编译。

既然 Kotlin 允许使用 null 值,为什么支持者们会鼓吹它具有 null 安全性呢?因为编译器会调用可能为 null 值(即可为空类型)的成员。

val str: String? = getNullableString()val int: Int? = str.toIntOrNull()      #1     

#1 无法通过编译。

修复上述代码的方式是,在调用成员之前,先检查变量是否为 null:

val str: String? = getNullableString()val int: Int? = if (str == null) null          else str.toIntOrNull()

这种方法很模式化,Kotlin 提供了 null 安全的运算符:

val str: String? = getNullableString()val int: Int? = str?.toIntOrNull()


Java 中的 Null 安全性


如上,我们讨论了 Kotlin 管理 Null 值的方法,下面我们来看看 Java。

首先,Java 中既没有不可为 null 的类型,也没有 null 安全的运算符。因此,每个变量都有可能为 null,而且我们也的确应该如此思考。

MyString str = getMyString();         #1  Integer anInt = null;                 #2if (str != null) {    anInt = str.toIntOrNull();}

#1 String 没有 toIntOrNull() 方法,所以我们假设 MyString 是一个包装类型,实际的操作交给 String。

#2 这里必须使用可变引用。

如果将多个调用放在一起,结果更糟,因为每个返回值都有可能为 null。为了安全着想,我们需要检查每个方法调用返回的值是否为 null。如下代码片段有可能抛出异常 NullPointerException:

var baz = getFoo().getBar().getBaz();

修复方法如下,但非常繁琐:

var foo = getFoo();var bar = null;var baz = null;if (foo != null) {    bar = foo.getBar();    if (bar != null) {        baz = bar.getBaz();    }}

出于这个原因,Java 8 引入了 Optional 类型。Optional 是一个包装,负责处理可能为 null 值的情况。在其他语言中,该类型被称为 Maybe、Option 等。

Java 语言的设计者建议,方法应返回:

  • 如果 X 不可能为 null,则返回类型 X;

  • 如果 X 可能为 null,则返回类型 Optional<X>。

如果我们将上述方法的返回类型改为 Optional,就可以编写出 null 安全的代码,而且还可以获得不可变性:

final var baz = getFoo().flatMap(Foo::getBar)                        .flatMap(Bar::getBaz)                        .orElse(null);

对于这种方法,我认为核心问题在于,Optional 本身可以为 null。Java 语言本身无法确保 Optional 不为 null。此外,方法的输入参数不建议使用 Optional。

为了解决这个问题,网上涌现了很多基于注解的库:

然而,不同的库,处理方式也不同:

  • Spring 会在编译时生成警告消息;

  • FindBugs 需要专门执行;

  • Lombok 会生成一段检查 null 的代码,如果变量无论如何都会为 null,则抛出异常 NullPointerException。


总结


当 Null 安全性不是一个大问题时,Java 可以被接受。因此,NullPointerException 异常会频繁发生。唯一安全的解决方案是将每个方法调用包装在 null 检查中。这种方式很有效,但同时也很模式化,代码也更加难以阅读。

开发人员称赞 Kotlin 带来了 Null 安全性,这是因为该语言的设计中融入了 Null 值处理机制。Java 这方面的处理远不如 Kotlin,因为 Java 语言架构师更加重视向后兼容性,而不是代码安全,这是设计上的决定。但是,作为一名开发人员,从 Null 安全性的角度出发,我认为 Kotlin 是比 Java 更有吸引力的选择。

☞复旦“首发”国产版 ChatGPT!名为 MOSS 惹群嘲,回应称:还非常不成熟从 ClickHouse 到 Apache Doris,腾讯音乐内容库数据平台架构演进实践

大学生快上车!大学教授要求用 ChatGPT 写论文

相关推荐

  • ofo小黄车已彻底无法登录;初代 iPhone 拍出 6.3 万美元天价;Linux 6.3 合并了硬件噪声工具|极客头条
  • 推荐一些基于 ChatGPT 的开源项目
  • Spring Boot 使用 ChatGPT API 开发一个聊天机器人
  • 年初面试,有点刺激了这波。。。
  • 通过 contextlib 模块详细复习一下 with 语句的用法
  • 聊聊Nacos
  • Win 11 新功能
  • 对ChatGPT的观察与思考
  • AI自给自足!用合成数据做训练,效果比真实数据还好丨ICLR 2023
  • 车万翔:ChatGPT时代,NLPer 的危与机
  • IM专题:分层架构IM系统(9)— Das核心职责和逻辑设计
  • 医院弄错试管婴儿让家长别计较;日本高端外国人才66%来自中国;公务员省考有省份扩招近 80%......|酷玩日爆
  • 什么男人一眼看上去很穷? ​| 每日一冷
  • “医保个账减少,增加5000家药店有何用?”
  • 宁德时代疯狂降价,电车们终于可以打骨折了?
  • 你想在价值10万元的床垫上滚床单吗?
  • 逼死代购!排队2小时才能买到的盖章版《紫禁城100》,我们原价包邮卖
  • 流浪地球周边到!2800+零件还原装甲车,科幻迷沸腾了!
  • 拜登秘密抵达乌克兰基辅;慈善主播在凉山向老人借钱摆拍“发钱”;俄亥俄一所金属工厂发生爆炸| 每日大新闻
  • 鼓励学生跟外国人“杂交”,讲台上站的都是什么人?