- 1. 前言
- 2. 认识 `Optional` 并使用
- 2.1. `Optional` 对象的创建
- 2.2. `Optional.get()` 方法
- 2.3. `Optional.isPresent()` 方法
- 2.4. `Optional.ifPresent()` 方法
- 2.5. `Optional.filter()` 方法
- 2.6. `Optional.map()` 方法
- 2.7. `Optional.orElse()` 方法
- 2.8. `Optional.orElseGet()` 方法
- 2.9. `Optional.orElseThrow()` 方法
- 3. 实战场景再现
- 4. `Optional` 使用注意事项
相信不少小伙伴已经被 Java 的 NPE(Null Pointer Exception) 所谓的空指针异常搞的头昏脑涨,有大佬说过“防止 NPE,是程序员的基本修养。”但是修养归修养,也是我们程序员最头疼的问题之一,那么我们今天就要尽可能的利用 Java 8 的新特性 Optional 来尽量简化代码同时高效处理 NPE(Null Pointer Exception)
2. 认识 Optional 并使用简单来说,Opitonal ([ˈɒpʃənl]) 类就是 Java 提供的为了解决大家平时判断对象是否为空用时会用 null != obj 这样的方式存在的判断,从而令人头疼导致 NPE(Null Pointer Exception),同时 Optional 的存在可以让代码更加简单,可读性跟高,代码写起来更高效
常规判断
// 对象 人
// 属性有 name,age
Person person = new Person();
if (null == person) {
return "person为null";
}
return person;
使用 Optional
// 对象 人
// 属性有 name,age
Person person = new Person();
return Optional.ofNullable(person).orElse("person为null");
测试展示类 Person 代码
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
下面,我们就高效的学习一下神奇的 Optional 类!
2.1. Optional 对象的创建首先我们先打开 Optional 的内部去一探究竟,先把几个创建 Optional 对象的方法提取出来
public final class Optional{ private static final Optional> EMPTY = new Optional<>(); private final T value; // 我们可以看到两个构造方格都是 private 私有的 // 说明我们没办法在外面去 new 出来 Optional 对象 private Optional() { this.value = null; } private Optional(T value) { this.value = Objects.requireNonNull(value); } // 这个静态方法大致 是创建出一个包装值为空的一个对象因为没有任何参数赋值 public static Optional empty() { @SuppressWarnings("unchecked") Optional t = (Optional ) EMPTY; return t; } // 这个静态方法大致 是创建出一个包装值非空的一个对象 因为做了赋值 public static Optional of(T value) { return new Optional<>(value); } // 这个静态方法大致是 如果参数 value 为空,则创建空对象,如果不为空,则创建有参对象 public static Optional ofNullable(T value) { return value == null ? empty() : of(value); } }
再做一个简单的实例展示 与上面对应
// 1、创建一个包装对象值为空的 Optional 对象 OptionaloptEmpty = Optional.empty(); // 2、创建包装对象值非空的 Optional 对象 Optional optOf = Optional.of("optional"); // 3、创建包装对象值允许为空,也可以不为空的 Optional 对象 Optional optOfNullable1 = Optional.ofNullable(null); Optional optOfNullable2 = Optional.ofNullable("optional");
我们关于创建 Optional 对象的内部方法大致分析完毕 接下来也正式的进入 Optional 的学习与使用中
2.2. Optional.get() 方法get() 方法是返回一个 Optional 的实例值,源码
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
- 如果 value 不为空则返回
- 如果为空则抛出异常 No value present
简单实例展示
Person person = new Person(); person.setAge(2); Optional.ofNullable(person).get();2.3. Optional.isPresent() 方法
isPresent() 方法就是会返回一个 boolean 类型值,如果对象不为空则为真,如果为空则为false,源码
public boolean isPresent() {
return value != null;
}
简单的实例展示
Person person = new Person();
person.setAge(2);
if (Optional.ofNullable(person).isPresent()){
// 写不为空的逻辑
System.out.println("不为空");
} else {
// 写为空的逻辑
System.out.println("为空");
}
2.4. Optional.ifPresent() 方法
这个意思是如果对象非空,则运行函数体,源码
public void ifPresent(Consumer super T> consumer) {
// 如果 value 不为空,则运行 accept 方法体
if (value != null)
consumer.accept(value);
}
看实例
Person person = new Person();
person.setAge(2);
Optional.ofNullable(person).ifPresent(p -> System.out.println("年龄"+p.getAge()));
如果对象不为空,则会打印这个年龄,因为内部已经做了 NPE(非空判断),所以就不用担心空指针异常了
2.5. Optional.filter() 方法filter() 方法大致意思是,接受一个对象,然后对他进行条件过滤,如果条件符合则返回 Optional 对象本身,如果不符合则返回空 Optional,源码
public Optionalfilter(Predicate super T> predicate) { Objects.requireNonNull(predicate); // 如果为空直接返回 this if (!isPresent()) return this; else // 判断返回本身还是空 Optional return predicate.test(value) ? this : empty(); }
简单实例
Person person = new Person(); person.setAge(2); Optional.ofNullable(person).filter(p -> p.getAge()>50);2.6. Optional.map() 方法
map() 方法将对应 Funcation 函数式接口中的对象,进行二次运算,封装成新的对象然后返回在 Optional 中,源码
public Optional map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
// 如果为空返回自己
if (!isPresent())
return empty();
else {
// 否则返回用方法修饰过的Optional
return Optional.ofNullable(mapper.apply(value));
}
}
实例展示
Person person1 = new Person();
person.setAge(2);
String optName = Optional.ofNullable(person).map(p -> person.getName()).orElse("name为空");
2.7. Optional.orElse() 方法
常用方法之一,这个方法意思是 如果包装对象为空的话,就返回参数对象 other,如果非空,则返回原对象 value,源码
public T orElse(T other) {
// 如果非空,返回 value,如果为空,返回 other
return value != null ? value : other;
}
实例
Person person1 = new Person();
person.setAge(2);
Optional.ofNullable(person).orElse(new Person("小明", 2));
2.8. Optional.orElseGet() 方法
这个与 orElse() 很相似,入参不一样,入参为 Supplier 对象,为空返回传入 对象的.get()方法,如果非空则返回当前对象,源码
public T orElseGet(Supplier extends T> other) {
return value != null ? value : other.get();
}
实例
Optional2.9. Optional.orElseThrow() 方法> sup = Optional.ofNullable(Person::new); // 调用get()方法,此时才会调用对象的构造方法,即获得到真正对象 Optional.ofNullable(person).orElseGet(sup.get());
方法作用:如果为空,就抛出你定义的异常,如果不为空返回当前对象
publicT orElseThrow(Supplier extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
实例:这个就贴实战源码了
// 简单的一个查询
Member member = memberService.selectByPhone(request.getPhone());
Optional.ofNullable(member).orElseThrow(() -> new ServiceException("没有查询的相关数据"));
3. 实战场景再现
场景1:在 Service 层中查询一个对象,返回之后判断是否为空并做处理
// 查询一个对象
Member member = memberService.selectByIdNo(request.getCertificateNo());
// 使用ofNullable加orElseThrow做判断和操作
Optional.ofNullable(member).orElseThrow(() -> new ServiceException("没有查询的相关数据"));
场景2:我们可以在 dao 接口层中定义返回值时就加上 Optional 例如:我使用的是 jpa,其他也同理
public interface LocationRepository extends JpaRepository{ Optional findLocationById(String id); }
然后在是 Service 中
public TerminalVO findById(String id) {
// 这个方法在dao层也是用了Optional包装了
Optional terminalOptional = terminalRepository.findById(id);
// 直接使用 isPresent() 判断是否为空
if (terminalOptional.isPresent()) {
// 使用 get()方法获取对象值
Terminal terminal = terminalOptional.get();
TerminalVO terminalVO = BeanCopyUtils.copyBean(terminal, TerminalVO.class);
// 调用 dao 层方法返回包装后的对象
Optional location = locationRepository.findLocationById(terminal.getLocationId());
if (location.isPresent()) {
terminalVO.setFullName(location.get().getFullName());
}
return terminalVO;
}
throw new ServiceException("该终端不存在");
}
4. Optional 使用注意事项
Optional 真么好用,真的可以完全替代 if 判断吗?我想这肯定是大家使用完之后 Optional 之后可能会产生的想法,答案是否定的
例子:如果我只想判断对象的某一个变量是否为空?
Person person = new Person();
person.setName("");
persion.setAge(2);
// 普通判断
if(StringUtils.isNotBlank(person.getName())){
// 名称不为空执行代码块
}
// 使用Optional做判断
Optional.ofNullable(person).map(p -> p.getName()).orElse("name为空");
我觉得这个例子就能很好的说明这个问题,只是一个很简单判断,如果用了 Optional 我们还需要考虑包装值,考虑代码书写,考虑方法调用,虽然只有一行,但是可读性并不好,如果别的程序员去读,我觉得肯定没有 if 看的明显



