java代码之美(16) ---Java8 Optional
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位"));
}
}
运行结果
参考
你如果愿意有所作为,就必须有始有终。(26)
java代码之美(16) ---Java8 Optional的更多相关文章
- java代码之美(17) ---Java8 LocalDateTime
Java8 LocalDateTime 在java8之前我们在处理时间的时候都是用的Date,但它其实有很明显的缺点. 1.我们也会对日期做一些操作,比如加几天.加几分,当月的最后一天等等.有些计算实 ...
- java代码之美(15)---Java8 Function、Consumer、Supplier
Java8 Function.Consumer.Supplier 有关JDK8新特性之前写了三篇博客: 1.java代码之美(1)---Java8 Lambda 2.java代码之美(2)---Jav ...
- java代码之美(14)---Java8 函数式接口
Java8 函数式接口 之前写了有关JDK8的Lambda表达式:java代码之美(1)---Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加 ...
- java代码(15) ---java8 Function 、Consumer 、Supplier
Java8 Function.Consumer.Supplier 有关JDK8新特性之前还有三篇博客: 1,java代码(1)---Java8 Lambda 2,java代码(2)---Java8 S ...
- java代码(14) --Java8函数式接口
Java8函数式接口 之前有关JDK8的Lambda表达式 Java代码(1)--Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加简洁 一.概 ...
- java代码之美(11)---java代码的优化
java代码的优化 随着自己做开发时间的增长,越来越理解雷布斯说的: 敲代码要像写诗一样美.也能理解有一次面试官问我你对代码有洁癖吗? 一段好的代码会让人看就像诗一样,也像一个干净房间会让人看去很舒服 ...
- java代码之美(3)---guava 复写Object常用方法
guava 复写Object常用方法 Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方 ...
- java代码之美(10)---Java8 Map中的computeIfAbsent方法
Map中的computeIfAbsent方法 Map接口的实现类如HashMap,ConcurrentHashMap,HashTable等继承了此方法,通过此方法可以在特定需求下,让你的代码更加简洁. ...
- java代码之美(2)---Java8 Stream
Stream 第一次看到Stream表达式就深深把我吸引,用它可以使你的代码更加整洁而且对集合的操作效率也会大大提高,如果你还没有用到java8的Stream特性,那就说明你确实out啦. 一.概述 ...
随机推荐
- 【重要】Pro Git 第二版 简体中文
不管是入门还是精通git,下面这本书都是必读,同时它也是官方推荐书籍. Pro Git 第二版 简体中文 我自己还收集了一份网页版的progit,但可能不是progit第二版. 下载地址 ...
- 【JavaScript学习笔记】函数、数组、日期
一.函数 一个函数应该只返回一种类型的值. 函数中有一个默认的数组变量arguments,存储着传入函数的所有参数. 为了使用函数参数方便,建议给参数起个名字. function fun1(obj, ...
- 2018 CCPC 网络赛
The Power Cube is used as a stash of Exotic Power. There are n cities numbered 1,2,…,n where allowed ...
- 4.JavaSE之标识符
标识符:Java所有的组成部分都需要名字.类名.变量名以及方法名都被称为标识符. 关键字:abstract.assert.boolean.breake.public.static.class...
- shh登入不能自动执行.bashrc
在linux 上新安装的anconda来管理软件,把环境变量放在home目录的.bashrc.结果每次开终端,不能直接使用conda.需要手动加环境变量. 用户登入后计算机执行了哪些文件 用户登录时b ...
- xsd 和 wsdl
xsd : 可用方便 不同的语言之间的 用命令行来 转换对应语言的. wsdl: 可用方便不同语言的类描述 用命令行 来相互转换. 类似 thift me ?
- NOI2.5 8783:单词接龙
描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的"龙"(每个单词都最多在"龙"中 ...
- [校内训练19_09_02]C
题意 给出一棵N 个节点的树,树上的每个节点都有一个权值$a_i$. 有Q 次询问,每次在树上选中两个点u, v,考虑所有在简单路径u, v 上(包括u, v)的点构成的集合S. 求$\sum_{w∈ ...
- mysql--->profile使用
Mysql分析-profile详解 简介 Profiling是从 mysql5.0.3版本以后才开放的. 启动profile之后,所有查询包括错误的语句都会记录在内. 此工具可用来查询SQL执行状态, ...
- linux系统CentOS7中find命令使用
一.作用 查找文件或目录 二.参数(常用) -atime 查找在指定时间曾被存取过的目录或文件,单位以24小时计算.(访问时间,执行文件等) -ctime 查找指定时间曾被更改的目录或文件,单位以24 ...