到目前为止,臭名昭著的空指针异常是由于Java应用程序失败的最常见原因。过去,为了解决空指针异常,Google著名的Guava项目引入了Optional。Guava鼓励程序员通过检查空值来防止代码污染。去谷歌 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();
结果会导致使用ifllpointerexceptions空指针是错误的 判断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";
}
我们的目的是为库方法的返回类型提供有限的机制,其中需要一种明确的方法来表示“无结果”,使用nulll绝对可能会导致错误。因此,添加了Optional ,这不是一个真正的新概念,历史可以追溯到Haskell的Maybel monad。突然之间,我们得到了JDK批准表示可能存在或不存在的值,以前推出的java的新特性是一样的,在各地使用新功能的开发人员欣喜若狂。
空引用空指针是无数错误的来源,通常无法很好地识别“不存在”这个概念。传入领域的所有变量都需要执行业务逻辑。我们必须减少if的编写(obj == null)检查判断的代码行数量。我们仍然需要与外部世界(数据库查询、REST端点等)交互,并根据执行逻辑进行交互输出。如果使用Optional 可以提供适当的帮助。
有一种诱惑是调用get()获取其中值。我们都知道普通Javabean的getter / setters :)。并且希望如果我们调用gett,我们会调用gett ...()我们会得到一些东西。调用普通bean的getter时,你永远不会得到任何异常抛出。但是,如果在optional上调用get方法,且选项内部为空,则抛出异常nosuchelementexception。
这些方法应该被称为getOrThorwSomeHorribleError()因此,第一条和第二条规则:
1、不要把nulll给Optionall
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
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、不要为了链法而使用optionall 。
使用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新特色专题课程,这里有你想学的一切。