动力节点旗下在线教育品牌  |  咨询热线:400-8080-105 学Java全栈,上蛙课网
首页 > 文章

java8新特性之Optional类

07-21 17:40 206浏览
举报 T字号
  • 大字
  • 中字
  • 小字

到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。到Google Guava的启发,java8新特性Optional类已经成为Java 8类库的一部分,作为java8的新特性横空出世。

Optional 类(java.util.Optional) 是一个容器类,它是一个box类型,保持对另一个对象的引用。Optional类是不可变的,不可序列化的,它代表一个值存在或不存在,在此之前一直都 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。

Optional隐藏了可能存在空指针的不确定性,比如:

List numbers= ImmutableList.of("ONE", "TWO", "THREE");

return numbers.stream()

 .filter(number -> "FOUR".equals(number))

 .findAny()

 .toLoweCase();

结果会导致NullPointerExceptions空指针错误,而使用if else进行判断是一种切割器cutter

List numbers= ImmutableList.of("ONE", "TWO", "THREE");

String numberThatImLookingFour =

numbers.stream()

 .filter(number -> "FOUR".equals(number))

 .findAny();

if(numberThatImLookingFour != null){

 return numberThatImLookingFour.toLowerCase();

}else{

 return "not found";

}

我们的目的是为库方法的返回类型提供一种有限的机制,其中需要一种明确的方式来表示“无结果”,并且对于这样的方法使用null绝对可能导致错误。因此添加了Optional ,这不是一个真正的新概念,历史可以追溯到Haskell的Maybe monad。突然间,我们获得了JDK批准的表示可能存在或不存在的值的方式,和以往推出的java新特性一样,在各地使用新功能的开发人员欣喜若狂

空引用空指针是无数个错误的来源,通常也不能很好地标识“不存在”这个概念。传入领域的所有变量是执行业务逻辑所需的,我们必须减少编写if(obj == null)检查判断的代码行数量。我们仍然需要与外部世界(数据库查询,REST端点等)进行交互,并根据执行的逻辑交互输出。如果使用Optional 得当则可以提供帮助。

有一种诱惑是调用get()来获取其中的值。我们都知道普通JavaBean的getter / setters :)。并且期望如果我们调用get ...()我们就会得到一些东西。当调用普通bean的getter时,你永远不会得到任何抛出的异常。但是,如果调用在optional上调用get方法,并且该选项内部为空时,则会抛出异常NoSuchElementException。

这些方法应该被称为getOrThorwSomeHorribleError(),因此第一和第二条规则:

1、不要将null赋给Optional

2、避免使用Optional.get()。如果你不能证明存在可选项,那么永远不要调用get()。

使用orElse(), orElseGet(), orElseThrow().获得你的结果。我们可以重构下面的代码:

String variable = fetchSomeVaraible();

if(variable == null){

 1. throw new IllegalStateException("No such variable");

 2. return createVariable();

 3. return "new variable";

} else {

 ...

 100 lines of code

 ...

}

重构到:

1.

Optional variableOpt = fetchOptionalSomeVaraible();

String variable = variableOpt.orElseThrow(() -> new Exeption(""))

... 100 lines of code ...

2.

Optional variableOpt = fetchOptionalSomeVaraible();

String variable = variableOpt.orElseGet(() -> createVariable())

... 100 lines of code ...

3.

Optional variableOpt = fetchOptionalSomeVaraible();

String variable = variableOpt.orElse("new variable")

... 100 lines of code ...

注意,orElse(..)是急切计算,意味着下面代码:

Optional optionalDog = fetchOptionalDog();

optionalDog

 .map(this::printUserAndReturnUser)

 .orElse(this::printVoidAndReturnUser)

如果值存在则将执行两个方法,如果值不存在,则仅执行最后一个方法。为了处理这些情况,我们可以使用方法orElseGet(),它将supplier 作为参数,并且是惰性计算的。

3、不要在字段,方法参数,集合中使用Optional。

下面是将thatField直接赋值给了类字段:

public void setThatField(Optional thatField){

  this.thatField = thatField;

}

改为:

setThatField(Optional.ofNullable(thatField));

4、只有每当结果不确定时,使用Optional作为返回类型我们的目的是为库方法的返回类型提供一种有限的机制,其中需要一种明确的方式来表示“无结果”,并且对于这样的方法使用null 绝对可能导致错误。

5、不要害怕使用map和filter。

有一些值得遵循的一般开发实践称为SLA-p:Single Layer of Abstraction字母的第一个大写。

下面是需要被重构代码:

Dog dog = fetchSomeVaraible();

String dogString = dogToString(dog);

public String dogToString(Dog dog){

 if(dog == null){

   return "DOG'd name is : " + dog.getName();

 } else {

   return "CAT";

 }

}

重构到:

Optional dog = fetchDogIfExists();

String dogsName = dog

 .map(this::convertToDog)

 .orElseGet(this::convertToCat)

public void convertToDog(Dog dog){

   return "DOG'd name is : " + dog.getName();

}

public void convertToCat(){

   return "CAT";

}

Filter是有用的折叠语法:

 

Dog dog = fetchDog();

if(optionalDog != null && optionalDog.isBigDog()){

  doBlaBlaBla(optionalDog);

}

上面代码可以被重构为:

Optional optionalDog = fetchOptionalDog();

optionalDog

 .filter(Dog::isBigDog)

 .ifPresent(this::doBlaBlaBla)

6、不要为了链方法而使用optional 。

使用optional 时要注意的一件事是链式方法的诱惑。当我们像构建器模式一样链接方法时,事情可能看起来很漂亮:)。但并不总是等于更具可读性。所以不要这样做:

 

Optional

 .ofNullable(someVariable)

 .ifPresent(this::blablabla)

它对性能不利,对可读性也不好。我们应尽可能避免使用null引用。

7、使所有表达式成为单行lambda

这是更普遍的规则,我认为也应该应用于流。使用Optional 重要点是记住等式左边和右边一样重要:

Optional

 .ofNullable(someVariable)

 .map(variable -> {

   try{

      return someREpozitory.findById(variable.getIdOfOtherObject());

   } catch (IOException e){

     LOGGER.error(e);

     throw new RuntimeException(e);

   }})

 .filter(variable -> {

   if(variable.getSomeField1() != null){

     return true;

   } else if(variable.getSomeField2() != null){

     return false;   

   } else {

     return true;

   }

  })

 .map((variable -> {

   try{

      return jsonMapper.toJson(variable);

   } catch (IOException e){

     LOGGER.error(e);

     throw new RuntimeException(e);

   }}))

 .map(String::trim)

 .orElseThrow(() -> new RuntimeException("something went horribly wrong."))

上面那么冗长代码块可以使用方法替代:

Optional

 .ofNullable(someVariable)

 .map(this::findOtherObject)

 .filter(this::isThisOtherObjectStale)

 .map(this::convertToJson)

 .map(String::trim)

 .orElseThrow(() -> new RuntimeException("something went horribly wrong."));

通过上面这么多的实例,相信我们已经深切体会到了Optional类的神奇之处,领略到了它的魅力。Java8 提供的Optional类使得Java程序更为安全,防止代码受到空值的污染。想要了解更多的java8新特性,可以观看本站的java8新特性专题课程,你想学的这里全都有。

0人推荐
共同学习,写下你的评论
0条评论
代码小兵1280
程序员代码小兵1280

11篇文章贡献54542字

作者相关文章更多>

推荐相关文章更多>

Java数据结构

HelloWorld10-31 08:24

浅谈MySQL中SQL优化的常用方法

军哥08-12 23:29

五分钟读懂UML类图

江湖人称小李白12-10 10:41

MyBatis开发框架的四大核心

IT逐梦者08-17 21:43

一次搞定continue,break和return

HelloWorld11-06 11:19

发评论

举报

0/150

取消