一句话介绍Optional类:使用JDK8的Optional类来防止NullPointerException(空指针异常)问题
。html
在咱们开放过程当中,碰到的异常中NullPointerException必然是排行第一的。因此在平时编码中,咱们会时时的判断null。java
public void saveCity(City city) { if (city != null) { String cityName = city.getCityName(); if (cityName != null) { String code = cityDao.findCodeByName(cityName); city.setCode(code); cityDao.save(city); } } }
虽然上面代码变得更加安全,可是过多嵌套 if 语句下降代码总体可读性,提升复杂度。咱们能够优化下代码编程
public void saveCity(City city) { if (city == null) { return; } String cityName = city.getCityName(); if (cityName == null) { return; } String code = cityDao.findCodeByName(cityName); city.setCode(code); cityDao.save(city); }
这样还能够,但咱们经过Optional变的更简洁api
public void saveCity(City city) { //就一行 city不为空返回 城市名称 不然直接返回空 Optional<String> roleOpt = Optional.ofNullable(city).map(City::getCityName); //若是容器中 不为空 if (roleOpt.isPresent()) { String code = cityDao.findCodeByName(roleOpt.get()); city.setCode(code); cityDao.save(city); } }
这样,咱们仅须要对咱们关心的作一次校验,省却了前面的一系列的检验操做。安全
概念
Optiona本质是一个容器,容器中存在为null或者不包含非null值的容器对象。提供了一系列的方法供咱们判断该容器里的对象是否存在。app
/** * final修饰表明不能被子类继承 */ public final class Optional<T> { /** * 建立一个空容器 */ private static final java.util.Optional<?> EMPTY = new java.util.Optional<>(); /** * 传入的值 */ private final T value; /** * 构造函数私有化 说明不能被外部new */ private Optional() { this.value = null; } /** * 私有化构造函数 */ private Optional(T value) { this.value = Objects.requireNonNull(value); } /** * 获取空容器 */ public static <T> java.util.Optional<T> empty() { @SuppressWarnings("unchecked") java.util.Optional<T> t = (java.util.Optional<T>) EMPTY; return t; } /** * 传入的对象不能为空 不然抛异常 */ public static <T> java.util.Optional<T> of(T value) { return new java.util.Optional<>(value); } /** * 传入的对象能够为空 */ public static <T> java.util.Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } /** * 获取容器对象的方法 注意 若是用这个方法则表明容器中必定有对象,不然抛异常 */ public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } /** * 判断容器对象是否为空 */ public boolean isPresent() { return value != null; } /** * 若是容器对象为空 则返回当前对象 */ public T orElse(T other) { return value != null ? value : other; } //==========有关下面这几个JDK8自带的函数式接口的做用,上一篇博客有详细说明,这里就很少说了。 /** * 传入Consumer编程式接口参数 */ public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); } /** * 传入Predicate编程式接口参数 */ public java.util.Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); } /** * 传入Function编程式接口参数 */ public <U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return java.util.Optional.ofNullable(mapper.apply(value)); } } /** * 传入Function编程式接口参数 */ public <U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } } /** * 传入Supplier编程式接口参数 */ public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } /** * 传入Supplier编程式接口参数 */ public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } } }
经过上面源码能够看出,Optional的构造函数都是私有化的,没法直接new对象。它这边提供了3个静态方法获取对象。函数
一、建立一个必定是空的Optional容器
优化
Optional<Car> optCar = Optional.empty();
二、建立一个必定是非空值Optional容器
(传入的对象不能够为null,不然抛出NullPointerException)ui
Optional<Car> optUser = Optional.of(user);
三、建立一个多是空也可能不为空的Optional容器
(传入的对象能够为null)this
Optional<Car> optUser = Optional.ofNullable(user);
一、isPresent() //有值则返回true 二、get(): //值存在时返回值,不然抛出一个NoSuchElement异常(因此调这个,通常先判断上面方法返回是否为true) 三、orElse(T other) //值存在时返回值,不然返回一个默认值 四、ifPresent(Consumer<T> block) //会在值存在的时候执行给定的代码块 五、orElseThrow(Supplier<? extends X> exceptionSupplier) //与get()相似,不一样的是能够自定义异常类型 六、orElseGet(Supplier<? extends T> other) //orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用 七、map/flatMap/filter //与Stream中用法相似
这里写一个针对以上API都涉及到的Demo,这个例子明白了,那么Optional的使用也就都清楚了。
代码
public class OptionalDemo { public static void main(String[] args) { //一、建立Optional实例,传入的对象不能为null Optional<String> nameOptional = Optional.of("张三"); //二、建立Optional实例,传入对象能够为null,也能够不weinull Optional emptyOptional = Optional.ofNullable(null); //三、isPresent方法用来检查Optional实例是否有值。 if (nameOptional.isPresent()) { //调用get()返回Optional值。 System.out.println("一、" + nameOptional.get()); } try { //四、在Optional实例上调用get()抛出NoSuchElementException。 System.out.println("二、" + emptyOptional.get()); } catch (NoSuchElementException ex) { System.out.println("三、异常" + ex.getMessage()); } // //五、若是Optional值不为空,lambda表达式会处理并在其上执行操做。(这里x表明就是nameOptional中的对象) nameOptional.ifPresent((x) -> { System.out.println("四、字符串长度为: " + x.length()); }); //六、若是有值orElse方法会返回Optional实例,没值则返回当前值 System.out.println("五、"+ emptyOptional.orElse("若是是空容器则返回李四")); System.out.println("六、"+nameOptional.orElse("若是是空容器则返回王五")); //七、orElseGet与orElse相似,区别在于传入的参数不一样,一个是直接传入对象,这个是传入Supplier函数式接口 System.out.println("七、" + emptyOptional.orElseGet(() -> "李四")); System.out.println("八、" + nameOptional.orElseGet(() -> "王五")); try { //八、若是是空容器,则能够抛出自定义异常。 emptyOptional.orElseThrow(() -> new NullPointerException("空容器异常")); } catch (Throwable ex) { System.out.println("九、" + ex.getMessage()); } Optional<String> ageOptional = Optional.of("10"); //九、这里入参是Function,因此能够转换容器中的对象 比如将String对象转为Integer对象 Optional<Integer> age = ageOptional.map((value) -> Integer.parseInt(value)); /** * 十、flatMap与map(Funtion)很是类似,不一样在于 map返回能够将String对象转为Integer对象,但flatMap转换后必定仍是String对象 */ Optional<String> upperName = nameOptional.flatMap((value) -> Optional.of(value.toUpperCase())); //十一、filter方法检查Optiona值是否知足给定条件。若是知足返回Optional实例值,不然返回空Optional。 Optional<String> longName = nameOptional.filter((value) -> value.length() > 6); System.out.println("十、" + longName.orElse("longName容器的名字长度小于6位")); //十二、另外一个示例,Optional知足给定条件。 Optional<String> anotherName = Optional.of("乌啦啦市长公主"); Optional<String> shortName = anotherName.filter((value) -> value.length() > 6); System.out.println("十一、" + shortName.orElse("anotherName容器的名字长度小于6位")); } }
运行结果
你若是愿意有所做为,就必须善始善终。(26)