乐猿社区,程序员的花果山
那些年那NullPointerException
做为一个javaer的老司机,可以回忆一下,在咱的码字生涯中到底遇到过多少次java.lang.NullPointerException异常?NullPointerException作为一个RuntimeException级别的异常不用显示捕获,若不小心处理我们经常会在生产日志中看到各种由NullPointerException引起的异常堆栈输出。
我们看看下面这个代码,用很传统很标准的Java编码风格编写:
String result = doSomething();String [] str = result.split(",");
这一段代码很简单,日常的业务代码肯定比这个复杂的多,而实际上我们大量的Java编码都是按这种套路编写的,但是如果doSomething返回的是一个null,那么可以看出最终肯定会抛出NullPointerException。在我们编写业务代码时,很少会想到要处理这个可能会出现的null,直到我们到了某个测试阶段,突然蹦出一个NullPointerException异常,我们才意识到原来我们得像下面这样加一个判断来搞定这个可能会返回的null值。
String result = doSomething();if(result!=null){ String [] str = result.split(",");}
利用Optional实现Java函数式编程
眼看像Scala等基于jvm的语言都有类似的语法糖了,java作为一统江湖的老大哥,怎么可能会没有自已的必杀技呢?新版本的Java,比如Java 8引入了一个新的Optional类。Optional类的Javadoc描述如下:
它是一个容器,装载着非NULL元素(或者没有装载元素),提供了一系列的方法供我们判断该容器里的对象是否存在(以及后续的操作)。
对于上段代码,采用正确姿势Optional后变身为:
String result = doSomething();String[] str = Optional.ofNullable(result).map(str->str.split(",")).orElse(new String []{});
Optional常用必杀持
of
为非null的值创建一个Optional。
of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException
/调用工厂方法创建Optional实例Optionalname = Optional.of("乐猿社区");//传入参数为null,抛出NullPointerException.Optional someNull = Optional.of(null);
ofNullable
为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。
ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。示例如下:
//下面创建了一个不包含任何值的Optional实例//例如,值为'null'Optional empty = Optional.ofNullable(null);
isPresent
如果值存在返回true,否则返回false
//isPresent方法用来检查Optional实例中是否包含值if (name.isPresent()) { //在Optional实例内调用get()返回已存在的值 System.out.println(name.get());//输出乐猿社区}
get
如果Optional有值则将其返回,否则抛出NoSuchElementException
try { Optional empty = Optional.ofNullable(null); //在空的Optional实例上调用get(),抛出NoSuchElementException System.out.println(empty.get());} catch (NoSuchElementException ex) { System.out.println(ex.getMessage());//输出:No value present }
ifPresent
如果Optional实例有值则为其调用consumer,否则不做处理
要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接通过lambda表达式传入参数。
如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码:
//ifPresent方法接受lambda表达式作为参数。//lambda表达式对Optional的值调用consumer进行处理。name.ifPresent((value) -> { System.out.println("The length of the value is: " + value.length());});
orElse
如果有值则将其返回,否则返回指定的其它值。
如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。示例如下:
//如果值不为null,orElse方法返回Optional实例的值。//如果为null,返回传入的消息。//输出:There is no value present!System.out.println(empty.orElse("There is no value present!"));//输出:SanaullaSystem.out.println(name.orElse("There is some value!"));
orElseGet
orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。示例如下:
//orElseGet与orElse方法类似,区别在于orElse传入的是默认值,//orElseGet可以接受一个lambda表达式生成默认值。//输出:Default ValueSystem.out.println(empty.orElseGet(() -> "Default Value"));//输出:SanaullaSystem.out.println(name.orElseGet(() -> "Default Value"));
orElseThrow
如果有值则将其返回,否则抛出supplier接口创建的异常。
在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。示例如下:
try { //orElseThrow与orElse方法类似。与返回默认值不同, //orElseThrow会抛出lambda表达式或方法生成的异常 empty.orElseThrow(Exception::new);} catch (Throwable ex) { //输出: No value present in the Optional instance System.out.println(ex.getMessage());}
map
如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional
map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。map方法示例如下:
//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。OptionallowerName = name.map((value) -> value.toLowerCase());System.out.println(lowerName.orElse("No value found"));
flatMap
如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。
flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。
//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。//但flatMap方法中的lambda表达式返回值必须是Optionl实例。OptionallowerName = name.flatMap((value) -> value.toLowerCase());System.out.println(lowerName.orElse("No value found"));
filter
filter个方法通过传入限定条件对Optional实例的值进行过滤
filter需要传入一个lambda表达式。对于filter函数我们应该传入实现了Predicate接口的lambda表达式。
//filter方法检查给定的Option值是否满足某些条件。//如果满足则返回同一个Option实例,否则返回空Optional。OptionallongName = name.filter((value) -> value.length() > 4);System.out.println(longName.orElse("The name is less than 4 characters"));//输出乐猿社区 //另一个例子是Optional值不满足filter指定的条件。Optional anotherName = Optional.of("乐猿");Optional shortName = anotherName.filter((value) -> value.length() > 6);//输出:name长度不足4字符System.out.println(shortName.orElse("The name is less than 6 characters"));
总结
通过filter,map 和 flatMap之类的函数可以将其安全的进行变换,最后通过orElse系列,get,isPresent 和 ifPresent将其中的值提取出来,从而避免了NullPointerException,让我们的代码看起来更加优雅。
写在最后的
Optional只是Java函数式编程的冰山一角,需要结合lambda、stream、Funcationinterface等特性才能真正的了解Java8函数式编程的效用。
鼓励把新的Java8特性引入到目前的项目中,一个长期配合的团队以及一门古老的编程语言都需要不断的注入新活力,否则不进则退