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. 【重要】Pro Git 第二版 简体中文

    不管是入门还是精通git,下面这本书都是必读,同时它也是官方推荐书籍.   Pro Git 第二版 简体中文     我自己还收集了一份网页版的progit,但可能不是progit第二版. 下载地址  ...

  2. 【JavaScript学习笔记】函数、数组、日期

    一.函数 一个函数应该只返回一种类型的值. 函数中有一个默认的数组变量arguments,存储着传入函数的所有参数. 为了使用函数参数方便,建议给参数起个名字. function fun1(obj, ...

  3. 2018 CCPC 网络赛

    The Power Cube is used as a stash of Exotic Power. There are n cities numbered 1,2,…,n where allowed ...

  4. 4.JavaSE之标识符

    标识符:Java所有的组成部分都需要名字.类名.变量名以及方法名都被称为标识符. 关键字:abstract.assert.boolean.breake.public.static.class...

  5. shh登入不能自动执行.bashrc

    在linux 上新安装的anconda来管理软件,把环境变量放在home目录的.bashrc.结果每次开终端,不能直接使用conda.需要手动加环境变量. 用户登入后计算机执行了哪些文件 用户登录时b ...

  6. xsd 和 wsdl

    xsd : 可用方便 不同的语言之间的 用命令行来 转换对应语言的. wsdl: 可用方便不同语言的类描述 用命令行 来相互转换. 类似 thift me ?

  7. NOI2.5 8783:单词接龙

    描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的"龙"(每个单词都最多在"龙"中 ...

  8. [校内训练19_09_02]C

    题意 给出一棵N 个节点的树,树上的每个节点都有一个权值$a_i$. 有Q 次询问,每次在树上选中两个点u, v,考虑所有在简单路径u, v 上(包括u, v)的点构成的集合S. 求$\sum_{w∈ ...

  9. mysql--->profile使用

    Mysql分析-profile详解 简介 Profiling是从 mysql5.0.3版本以后才开放的. 启动profile之后,所有查询包括错误的语句都会记录在内. 此工具可用来查询SQL执行状态, ...

  10. linux系统CentOS7中find命令使用

    一.作用 查找文件或目录 二.参数(常用) -atime 查找在指定时间曾被存取过的目录或文件,单位以24小时计算.(访问时间,执行文件等) -ctime 查找指定时间曾被更改的目录或文件,单位以24 ...