Java8 Optional

一句话介绍Optional类:使用JDK8的Optional类来防止NullPointerException(空指针异常)问题

一、前言

在我们开放过程中,碰到的异常中NullPointerException必然是排行第一的。所以在平时编码中,我们会时时的判断null

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变的更简洁

    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);
}
}

这样,我们仅需要对我们关心的做一次校验,省却了前面的一系列的检验操作。

二、Optional API

概念 Optiona本质是一个容器,容器中存在为null或者不包含非null值的容器对象。提供了一系列的方法供我们判断该容器里的对象是否存在。

1、JDK源码

/**
* 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();
}
}
}

2、创建Optional对象

通过上面源码可以看出,Optional的构造函数都是私有化的,无法直接new对象。它这边提供了3个静态方法获取对象。

1、创建一个一定是空的Optional容器

Optional<Car> optCar = Optional.empty();

2、创建一个一定是非空值Optional容器(传入的对象不可以为null,否则抛出NullPointerException)

Optional<Car> optUser = Optional.of(user);

3、创建一个可能是空也可能不为空的Optional容器(传入的对象可以为null)

Optional<Car> optUser = Optional.ofNullable(user);

3、总结常用方法

1、isPresent()        //有值则返回true
2、get(): //值存在时返回值,否则抛出一个NoSuchElement异常(所以调这个,一般先判断上面方法返回是否为true)
3、orElse(T other) //值存在时返回值,否则返回一个默认值
4、ifPresent(Consumer<T> block) //会在值存在的时候执行给定的代码块
5、orElseThrow(Supplier<? extends X> exceptionSupplier) //与get()类似,不同的是可以自定义异常类型
6、orElseGet(Supplier<? extends T> other) //orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用
7、map/flatMap/filter //与Stream中用法类似

三、完整的示例

这里写一个针对以上API都涉及到的Demo,这个例子明白了,那么Optional的使用也就都清楚了。

代码

public class OptionalDemo {
public static void main(String[] args) {
//1、创建Optional实例,传入的对象不能为null
Optional<String> nameOptional = Optional.of("张三"); //2、创建Optional实例,传入对象可以为null,也可以不weinull
Optional emptyOptional = Optional.ofNullable(null); //3、isPresent方法用来检查Optional实例是否有值。
if (nameOptional.isPresent()) {
//调用get()返回Optional值。
System.out.println("1、" + nameOptional.get());
} try {
//4、在Optional实例上调用get()抛出NoSuchElementException。
System.out.println("2、" + emptyOptional.get());
} catch (NoSuchElementException ex) {
System.out.println("3、异常" + ex.getMessage());
} //
//5、如果Optional值不为空,lambda表达式会处理并在其上执行操作。(这里x代表就是nameOptional中的对象)
nameOptional.ifPresent((x) -> {
System.out.println("4、字符串长度为: " + x.length());
}); //6、如果有值orElse方法会返回Optional实例,没值则返回当前值
System.out.println("5、"+ emptyOptional.orElse("如果是空容器则返回李四"));
System.out.println("6、"+nameOptional.orElse("如果是空容器则返回王五")); //7、orElseGet与orElse类似,区别在于传入的参数不同,一个是直接传入对象,这个是传入Supplier函数式接口
System.out.println("7、" + emptyOptional.orElseGet(() -> "李四"));
System.out.println("8、" + nameOptional.orElseGet(() -> "王五")); try {
//8、如果是空容器,则可以抛出自定义异常。
emptyOptional.orElseThrow(() -> new NullPointerException("空容器异常"));
} catch (Throwable ex) {
System.out.println("9、" + ex.getMessage());
} Optional<String> ageOptional = Optional.of("10");
//9、这里入参是Function,所以可以转换容器中的对象 好比将String对象转为Integer对象
Optional<Integer> age = ageOptional.map((value) -> Integer.parseInt(value));
/**
* 10、flatMap与map(Funtion)非常相似,不同在于 map返回可以将String对象转为Integer对象,但flatMap转换后一定还是String对象
*/
Optional<String> upperName = nameOptional.flatMap((value) -> Optional.of(value.toUpperCase())); //11、filter方法检查Optiona值是否满足给定条件。如果满足返回Optional实例值,否则返回空Optional。
Optional<String> longName = nameOptional.filter((value) -> value.length() > 6);
System.out.println("10、" + longName.orElse("longName容器的名字长度小于6位")); //12、另一个示例,Optional满足给定条件。
Optional<String> anotherName = Optional.of("乌啦啦市长公主");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
System.out.println("11、" + shortName.orElse("anotherName容器的名字长度小于6位")); }
}

运行结果

参考

1、JDK8新特性之:Optional

2、Optional类包含的方法介绍及其示例

你如果愿意有所作为,就必须有始有终。(26)

java代码之美(16) ---Java8 Optional的更多相关文章

  1. java代码之美(17) ---Java8 LocalDateTime

    Java8 LocalDateTime 在java8之前我们在处理时间的时候都是用的Date,但它其实有很明显的缺点. 1.我们也会对日期做一些操作,比如加几天.加几分,当月的最后一天等等.有些计算实 ...

  2. java代码之美(15)---Java8 Function、Consumer、Supplier

    Java8 Function.Consumer.Supplier 有关JDK8新特性之前写了三篇博客: 1.java代码之美(1)---Java8 Lambda 2.java代码之美(2)---Jav ...

  3. java代码之美(14)---Java8 函数式接口

    Java8 函数式接口 之前写了有关JDK8的Lambda表达式:java代码之美(1)---Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加 ...

  4. java代码(15) ---java8 Function 、Consumer 、Supplier

    Java8 Function.Consumer.Supplier 有关JDK8新特性之前还有三篇博客: 1,java代码(1)---Java8 Lambda 2,java代码(2)---Java8 S ...

  5. java代码(14) --Java8函数式接口

    Java8函数式接口 之前有关JDK8的Lambda表达式 Java代码(1)--Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加简洁 一.概 ...

  6. java代码之美(11)---java代码的优化

    java代码的优化 随着自己做开发时间的增长,越来越理解雷布斯说的: 敲代码要像写诗一样美.也能理解有一次面试官问我你对代码有洁癖吗? 一段好的代码会让人看就像诗一样,也像一个干净房间会让人看去很舒服 ...

  7. java代码之美(3)---guava 复写Object常用方法

    guava 复写Object常用方法 Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方 ...

  8. java代码之美(10)---Java8 Map中的computeIfAbsent方法

    Map中的computeIfAbsent方法 Map接口的实现类如HashMap,ConcurrentHashMap,HashTable等继承了此方法,通过此方法可以在特定需求下,让你的代码更加简洁. ...

  9. java代码之美(2)---Java8 Stream

    Stream 第一次看到Stream表达式就深深把我吸引,用它可以使你的代码更加整洁而且对集合的操作效率也会大大提高,如果你还没有用到java8的Stream特性,那就说明你确实out啦. 一.概述 ...

随机推荐

  1. 快到极致的Android模拟器——Genymotion

    转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-)http://my.oschina.net/ryanhoo/blog/141824 还在用Androi ...

  2. DWVA-关于反射型xss的漏洞详解<xss reflected>

    反射型xss low级别 代码如下: <?php header ("X-XSS-Protection: 0"); // Is there any input? if( arr ...

  3. 【Spring Boot 源码解读】之 【为何引入了 Jedis 依赖最后用的还是 Lettuce 客户端?】

    1.Spring Boot 2.x 的两种 Redis 客户端 首先,我们都知道,从 Spring Boot 2.x 开始 Lettuce 已取代 Jedis 成为首选 Redis 的客户端.当然 S ...

  4. python集合的运算

    &  交集  | 并集    - 差集  ^ 异或集 # 在对集合做运算时,不会影响原来的集合,而是返回一个运算结果 # 创建两个集合 s = {1,2,3,4,5} s2 = {3,4,5, ...

  5. cogs 1001. [WZOI2011 S3] 消息传递 Tarjan

    1001. [WZOI2011 S3] 消息传递 ★★   输入文件:messagew.in   输出文件:messagew.out   简单对比时间限制:1 s   内存限制:128 MB Prob ...

  6. PBFT && RBFT算法流程

    PBFT && RBFT算法流程以及其实现(上) 这篇文章主要是讲一下RBFT中共识算法流程以及节点的加入的流程.在下一篇博客中,将使用Java实现该算法. 传统的PBFT算法无法动态 ...

  7. Redis 分布式锁的正确实现方式( Java 版 )

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  8. Numpy科学计算从放弃到入门

    目录 一.什么是Numpy ndarray对象 相关文档 二.如何创建数组 三.如何访问数组 下标索引 切片索引 布尔型索引 整数索引 方形索引 四.如何做算数运算 五.如何使用统计函数 六.数组转置 ...

  9. 编译游戏库allegro

    一个allegro依赖了大概十个库,还得自己一个个的去编译,然后复制粘贴 主要从两个网页学到的 第一个网页里有绝大多数的依赖库的编译方法 http://wiki.allegro.cc/index.ph ...

  10. Object类、常用API_2

    主要内容 Object类 Date类 DateFormat类 Calendar类 System类 StringBuilder类 包装类 学习目标 -[ ] 能够说出Object类的特点 -[ ] 能够 ...