也谈谈Optional

版权声明:所有文章版权所有 带刀码客,转发需附原文链接。 https://blog.csdn.net/anjing1225/article/details/84785554

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还有两个问题:

  1. Optional会使得堆的管理和调试堆栈更加不清晰。使用Optional,原本只需要一个对象指针,现在需要两个。lambda表达式会导致调试堆栈混乱,不好debug。
  2. 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);

    });
}

猜你喜欢

转载自blog.csdn.net/anjing1225/article/details/84785554