首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

再见空指针,Java 拟引入的全新语法,太香了~

作为一名Java开发者,我一直觉得Java在处理null方面就像走在雷区,时刻小心翼翼地避开那些可能爆炸的“空指针异常”。

你一定也遇到过这种情况:明明某个对象被认为不可能是null,但一跑程序,咣当一声就给你一个NullPointerException(NPE),然后只好埋头去Debug。

说实话,null的确很让人烦。你以为你写的代码逻辑严密,结果可能因为一个不经意的null引发了一连串的bug。更糟糕的是,Java语言本身的类型系统并没有明确表达一个变量是否能接受null。

比如,String s;这句声明到底是表示null合法呢?还是说根本就不能是null?要搞清楚这点,你得花不少时间去看业务逻辑甚至是源码。

那么问题来了,Java现在终于要出手了!新特性带来的非空限制类型(Null-Restricted Type)和可空类型(Nullable Type)可以直接在类型定义中明确指示:这个变量到底能不能为null。

这对所有Java程序员来说,绝对是个振奋人心的消息,因为它从根本上让我们能够在编译时就规避掉大部分的null指针异常。

而不是像现在这样等到运行时才能发现,甚至在最坏的情况下,这个bug可能会直接带崩你上线的生产环境——没有比这更令人抓狂的事情了。

新特性的语法是什么样的呢?其实很简单,就在类型后面加个符号表示即可。用Foo!表示不允许null的类型,Foo?表示可以接受null的类型,而Foo依然维持当前Java的传统——不明确表示是否接受null。

这意味着,我们终于可以用一种简单、直接的方式告诉编译器:这个变量绝对不会是null,要是我试图给它赋值null,请直接编译报错。

我们先来看一个简单的例子。假设我们有一个方法用来获取某个商品的名称,如果这个商品名称可能不存在,我们现在通常是这样写的:

public String getProductName(Product product) {

if (product == null) {

return null;

}

return product.getName();

}

这是我们在老版本Java里不得不采用的写法,原因是我们无法确定product是否为null。但如果使用新特性,你可以这样写:

public String! getProductName(Product! product) {

return product.getName();

}

上面的代码表示product不可能是null,如果有人在调用getProductName时传递了null,编译器会直接报错,而不是像以前那样可能导致运行时异常。同理,String!表示返回值也不可能是null。

这种写法看似简单,却极大提高了代码的安全性,因为我们可以在编译时就排除掉那些无处不在的空指针异常,而不需要在运行时祈祷这些变量千万不要是null。

不过,如果有些场景中,我们的确需要处理null该怎么办?这时候就可以用Foo?来表示可空类型。例如:

public String? getProductName(Product? product) {

return product == null ? null : product.getName();

}

使用了Product?后,我们可以明确表达product可能为null,而且编译器会在后续使用中自动进行检查:如果尝试直接调用product.getName(),编译器就会给你警告,提醒你:你可能忘了判断product是不是null哦!这样就避免了传统Java中那些令人头疼的NPE问题。

当然,你可能会问,这些新特性引入后会不会破坏现有代码呢?答案是不会。Java团队考虑了现有代码的兼容性,新特性是逐步引入的,开发者可以选择性地在自己的项目中启用这些特性,而不需要一上来就全面重构所有代码。

这意味着,你可以在不改动老代码的前提下,在新项目或者新模块中引入Foo!和Foo?,逐步提高项目的安全性。

新特性除了在类型定义时明确限制null,还会在类型转换时插入额外的检测逻辑。例如,如果你把一个Foo?类型转换成Foo!类型,编译器会在转换时自动插入检测代码,防止你在运行时传递null。举个例子:

String? nullableString = getNullableString();

String! nonNullableString = (String!) nullableString;

在这段代码中,如果nullableString的值是null,那么在转换成String!时编译器会插入额外的检查逻辑,如果真的发生了null,它会直接抛出异常,而不是让这个null值偷偷溜进String!类型中。

这种严格的空值检查同样适用于字段和数组。比如,如果你声明了一个String![]类型的数组,那么在初始化时,编译器会要求你为数组中的每个元素都提供非null的初始值,否则直接编译报错。

这种严格初始化机制让你在编写代码时不敢再有侥幸心理:所有非null限制类型的变量都必须被初始化,否则你就别想通过编译。

新特性中还有一项很重要的特性叫做“灵活的null转换”,它类似于Java中常见的装箱和拆箱机制,使得不同空值类型间的转换更加自然。

例如,String!可以轻松地转换成String?,因为一个非空字符串显然是可以放入允许null的类型中;但反过来,如果你把String?转换成String!,那么编译器就会很警觉地要求你插入null检查,确保安全。

那你可能会好奇,这些语法和TypeScript中处理null的方式有什么区别?在TypeScript中,我们也用过!和?这两个符号,不过用法上稍有不同。

在TypeScript中,!表示非空断言,意思是你强行告诉编译器“相信我,这个变量不会是null!”但这种断言只在编译时有效,运行时如果真遇到null还是会炸。

而在Java的新特性中,Foo!是从语言层面就明确表达了这个类型不允许是null,而且编译器会在每一次赋值、类型转换时都进行检查,确保没有null能混进来。

至于?在TypeScript中用来表示可选属性,而在Java的新特性中则是表示可空类型,这两个概念相近但不完全相同。Java的Foo?明确表示这个变量可以为null,而不仅仅是“可选”。

总的来说,Java此次引入的Null-Restricted和Nullable类型,算是从语言层面彻底宣战null,希望能够一劳永逸地解决开发者最头疼的空指针异常问题。

当然,这项特性目前还只是一个预览版,但我觉得它完全有希望成为未来Java中的标配。如果它能够成为正式特性,那么我们在编写Java代码时,处理null将不再是个噩梦,而是变得像其它编译时错误一样,可以轻松规避。

相信我,再也不用为追踪null指针异常熬夜了。这对于每一个写过Java代码的程序员来说,绝对是一个大大的福音。你说呢?是不是有点小激动?欢迎留言分享你的看法。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/Op05XWaDpmqkpWVDhieSgQAQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券