本章内容
口nu11引用引发的问题 ,以及为什么要避免nu11引用从nu11到optiona1:以nu11安全的方式重写你的域模型让optiona1发光发热:去除代码中对nu11的检查
读取optiona1中可能值的几种方法
口对可能缺失值的再思考
如果你作为Java程序员曾经遭遇过Nu11PointerException,请举起手。如果这是你最常遭遇的异常,请继续举手。非常可惜,这个时刻,我们无法看到对方,但是我相信很多人的手这个时刻是举着的。我们还猜想你可能也有这样的想法:"毫无疑问,我承认,对任何一位Java程序员来说,无论是初出茅庐的新人,还是久经江湖的专家,ullPointerexception都是他心中的痛,可是我们又无能为力,因为这就是我们为了使用方便甚至不可避免的像nu11引用这样的构造所付出的代价。"这就是程序设计世界里大家都持有的观点,然而,这可能并非事实的全
部真相,只是我们根深蒂固的一种偏见。1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了nu11引用的想法。ALGOLW是第一批在堆上分配记录的类型语言之一。Hoare选择nu11引用这种方式,"只是因为这种方法实现起来非常容易"。虽然他的设计初衷就是要"通过编译器的自动检测机制,确保所有使用引用的地方都是绝对安全的",他还是决定为nu11引用开个绿灯,因为他认为这是为"不存在的值"建模最容易的方式。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为"我价值百万的重大失误"。我们已经看到它带来的后果--程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现我们查看的并不是一个对象,而是一个空指针它会立即抛出一个让人厌烦的NullPointerException异常。
实际上,Hoare的这段话低估了过去五十年来数百万程序员为修复空引用所耗费的代价。近十年出现的大多数现代程序设计语言",包括Java,都采用了同样的设计方式,其原因是为了与
10.1.2 nu11 带来的种种问题
让我们一起回顾一下到目前为止进行的讨论,在Java程序开发中使用nu11会带来理论和实际操作上的种种问题。
口它是错误之源。
Nu11PointerException是目前Java程序开发中最典型的异常口它会使你的代码膨胀。
它让你的代码充斥着深度嵌套的nu11检查,代码的可读性糟糕透顶。
口它自身是毫无意义的。
nu11自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。
口它破坏了Java的哲学。
Java一直试图避免让程序员意识到指针的存在,唯一的例外是:nu11指针口它在Java的类型系统上开了个口子。
nu11并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个nu11变量最初的赋值到底是什么类型。
为了解业界针对这个问题给出的解决方案,我们一起简单看看其他语言提供了哪些功能。
10.4.1 用 optional 封装可能为 null 的值
现存Java API几乎都是通过返回一个nu11的方式来表示需要值的缺失,或者由于某些原因计算无法得到该值。比如,如果Map中不含指定的键对应的值,它的get方法会返回一个nu11。但是,正如我们之前介绍的,大多数情况下,你可能希望这些方法能返回一个optiona1对象。你无法修改这些方法的签名,但是你很容易用optiona1对这些方法的返回值进行封装。我们接着用Map做例子,假设你有一个Map<string,object>方法,访问由key索引的值时,如果map中没有与key关联的值,该次调用就会返回一个nu11。
Object value =map.get("key");
使用optional封装map的返回值,你可以对这段代码进行优化。要达到这个目的有两种方式:你可以使用笨拙的if-then-else判断语句,毫无疑问这种方式会增加代码的复杂度;或者
10.4 使用optional的实战示例217
你可以采用我们前文介绍的optional.ofNu1lable方法:
Optional<Object> value = Optional.ofNullable(map.get ("key"));
每次你希望安全地对潜在为nu11的对象进行转换,将其替换为optiona1对象时,都可以考虑使用这种方法。