Java 8中有一个名为Optional类的新功能,它号称可以解决NPE问题。很明显,一个Java对象实际上只是一个指针,而指针可以指向null。也许新世纪的大多数学计算机的毕业生从未真正学过指针,因为学校已经倾向于使用高级编程语言。但是这无所谓,就像上个世纪90年代 - 很久以前了的感觉 - 计算机的毕业生也不知道什么是COBOL。
本文结论是 Optional 也不能完全解决NPE,由于该类有以下问题,所以会带来更多的负面影响。喜欢Optional的同学适量取用吧:
- 抛出NPE
- 本身也可以是null,引起NPE
- 增加堆的大小
- 让debug更麻烦
- 没法序列化,对使用XML或者JSON的客户端来说,没啥用。
下面是论据:
新的Optional类,它还是一个类嘛,只不过,它是一个包装类,里面包装了指向其他对象的指针,这个指针可以指向null,但是Optional本身的实例还是可以指向null。有点绕口,但是学过Java的大概知道说的什么意思了,Optional也可以是null,不会在真正意义上解决NPE问题。
看下面两段代码:
public Optional<String> getNullOptionalString(){
return(null);
}
public Optional<String> getOptionalNullString(){
return Optional.ofNullable(null);
}
第一方法返回的还是一个null,第二个方法返回的是包装了null的Optional对象。所以下面哪个会报NPE?
String s1 = getNullOptionalString().get().toString();
String s2 = getOptionalNullString().get().toString();
答案显而易见,都会报错,第一个报错在get()之前,第二个报错在toString()之前。所以Optional怎么解决NPE问题?
有小伙伴估计就要说了,通过isPresent()呀!是的:
if (getOptionalNullString().isPresent()){
String s3 = getOptionalNullString().get().toString();
}
请问这个跟 if ( string != null ) { do something;} 有什么差别?
好吧,也许Optional不是这么用的。也许正确的用法是使用静态方法,比如Optional.of() 来创建一个Optional实例,但是这个方法有个问题,如果你传递的参数是null 的话, 直接会NPE。所以如果要避免NPE,使用这个方法的时候,我就已经知道传递给Optional的是一个非空值,那还要Optional作甚?这个case代码是这样的:
try{
Optional<Some> x =Optional.of(somethingReturnNotNull);
if(x.isPresent()){
System.out.println(x.get().toString());
}
}catch(NullPointerException x){
System.out.println(“NPE, Optional 干啥了?咋还有NPE?”);
}
好吧,客官又要说,我们可以用ofNullable(),是的,这个可以避免上面那个情况出现,而且可以通过ifPresent来避免繁重的检查指针是否为空的操作,并且可以很花哨的写成一行代码:
Optional.ofNullable(whateverSomeClassInstance).ifPresent(s->{System.out.println(s.toString());});
我非常困惑,什么样的人会喜欢这种写法而不是下面这个清晰的:
if(whateverSomeClassInstance != null) {
System.out.println(whateverSomeClassInstance.toString());
}
也许在Optional的ifPresent中使用lambda表达式操作让他们觉得自己是l337。
Optional还有两个问题:
- Optional会使得堆的管理和调试堆栈更加不清晰。使用Optional,原本只需要一个对象指针,现在需要两个。lambda表达式会导致调试堆栈混乱,不好debug。
- Optional是非可序列化对象,所以很多地方用不上。
至于为什么Optional不可序列化,这里有解答,翻译过来:
关于序列化的探讨,之前的实验已经覆盖了。JSR-335 的E/G两部分有非常明确的意愿:Optional除了作为“可选返回”语义的支持之外,不应该用到其他地方。(有的人甚至建议将它改名字为 OptionalReturn 来告诉用户设计这个类的初衷是什么;也许我们应该接受这个建议)我了解很多人希望Optional能做其他的事情。但是,并不是E/G部分简单的“忘记“了为Optional定义序列化,而是明确提出不应该这么做。
可是我认为连Optional的返回值都不需要,看看下面func1和func2,func1 有方法返回的捷径,有方法内部堆栈定义的new对象,可读性非常强。func2() 由于lambda的限制,只能在lambda内部new 对象,而且方法可读性差,没有明确的null return的逻辑。
public void func1(){
String s = getNullString();
if(s == null)
return;
SomethingNeedToModify sntm = new SomethingNeedToModify();
doSomethingElse1(s);
doSomethingElse2(sntm);
}
public void func2(){
Optional<String> s = getOptionalNullString();
SomethingNeedToModify sntm;
s.ifPresent(x->{
doSomethingElse1(x);
sntm = new SomethingNeedToModify();
doSomethingElse2(sntm);
});
}