Java-Optional类源码分析
- 2020 年 2 月 18 日
- 筆記
1.引出
我们在对象调用对象的方法、域的时候总是要进行判断对象是否为空的操作,即空指针异常(NullPointerException
)。 本质上,这是一个包含有可选值的包装类,这意味着 Optional
类既可以含有对象也可以为空。 Optional
是Java8提出的新特性,就是为解决空指针异常,方便函数式编程的新特性。
1.1空指针异常所需的常见操作
我们从一个简单的用例开始。在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException
:
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:
if (user != null) { Address address = user.getAddress(); if (address != null) { Country country = address.getCountry(); if (country != null) { String isocode = country.getIsocode(); if (isocode != null) { isocode = isocode.toUpperCase(); } } } }
你看到了,这很容易就变得冗长,难以维护。
2.Optional类的源码分析(JDK1.8)
package java.util; //1.以下4个接口的import用于lambda表达式传入的函数式编程,一个接口用于之后介绍的单独一个方法的执行 import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; public final class Optional<T> { //2.调用了4中的构造器,用于创建一个私有的value域指向null的Optional对象EMPTY(每个类只有一个且不能更改) //EMPTY之所以大写是因为其作为一个引用变量是不可变的。 private static final Optional<?> EMPTY = new Optional<>(); //3.用于存储传入的泛型对象或基本类型,如果空,则其值为null private final T value; //4.其为无参构造器:用于创建一个value=null的Optional的对象,而Optional对象不等于null,且返回的不是EMPTY域,而是再创建一个结构相同的对象返回 private Optional() { this.value = null; } //5.返回一个空的Optional对象,即EMPTY,其泛型类型会被转为调用时具体类型,尽管value指向null public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; } //6.其为带参数的构造器,返回一个指定非null值的Optional。requireNonNull方法是Objects类的泛型静态方法(不是Object类的方法,要注意),其用途就是构造器的输入参数value为空,则抛出空指针异常,否则,直接返回value. /* 相当于以下语句所达到的效果,但由于其可能会被反复调用,: * if (value == null) * throw new NullPointerException(); * return obj; * **/ private Optional(T value) { this.value = Objects.requireNonNull(value); } //7.of静态方法是Optional的静态工厂方法,这用于返回Optional的实例,但其输入value必须不为null,否则抛出空指针异常。 //而Optional的构造器都是私有的,无法使用new关键字构造其对象。 public static <T> Optional<T> of(T value) { return new Optional<>(value); } //8.ofNUllable字面意思上就能理解就是允许输入参数为null(Nullable)的of方法。当value==null时,调用empty()方法, //构造一个value值为null的Optional对象返回;否则,构造一个非空value值的optional对象返回。 public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } //9.get方法作为一个非静态方法,通常是被使用of/ofNullable静态方法的Optional对象所调用,返回其所存域:value。例如下面代码: //Optional中的大多非静态方法,都是由以下结构调用的; /** object:创建的非空对象,method则是属于object类的方法 * Optional.ofNullable(object).get().method(); **/ public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } //10.返回boolean值,用于value值是否为空的判断结果给出,注意,这不是判断Optional对象是否为空 public boolean isPresent() { return value != null; } //11.如果value≠null,则使用该值作为参数调用实现Consumer接口的lambda表达式;否则,不做任何事情。 //Consumer是一个有单个泛型输入参数、无返回值的一个由JDK提供的接口 public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); } //12.其为一个非静态方法,需要Option类的工厂方法返回对象来调用 //首先,输入接口类型参数(一般为lambda表达式)必须非空,否则爆空指针错误; //其次,判断optional对象的value值是否为空,是则直接返回optional对象; //第三步,判断存入的value是否匹配给定的 predicate; //是,则返回返回optional对象,否则返回新建的value为空的optional对象。 //综上,空指针异常出现在:lambda表达式为空,只有value非空且符合predicate才返回此optional对象,否则从结果上来看都是返回 //value==null的optional对象。 public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); } //Lambda为null则抛出空指针异常。如果value==null,则返回一个value==null的Optional对象;否则, //对其执行调用映射函数( mapper.apply(value) )得到返回值。如果返回值不为 null, //则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。 // public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } } //14.如果value ≠ null,则返回基于Optional包含的映射方法的值(mapper.apply(value)),否则(value==null)返回一个空的Optional //当Lambda表达式为空时,以及当映射返回值为null时,抛出空指针异常。 public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } } //15.如果value ≠ null,则返回value, 否则返回other。 public T orElse(T other) { return value != null ? value : other; } //16.如果value ≠ null,则返回value, 否则调用实现Supplier接口的对象,调用get方法,返回方法返回值(不一定是value)。 public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } //17.如果value ≠ null,则返回value,否则抛出由 Supplier 继承的异常 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } } //18.就是简单地对Object类的equals方法的重写,注意,Optional对象即使value值相同,也不会返回true,而是认为是不同的对象。 //不同于String中的equals方法。 @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Optional)) { return false; } Optional<?> other = (Optional<?>) obj; return Objects.equals(value, other.value); } //19.调用Objects类的静态方法hasCode,返回存在value的哈希码,如果value==null,则返回 0。 @Override public int hashCode() { return Objects.hashCode(value); } //20.返回一个Optional的value.tpString()方法的非空字符串,否则返回约定字符串:"Optional.empty" @Override public String toString() { return value != null ? String.format("Optional[%s]", value) : "Optional.empty"; } }
3.Optional类的真正作用:
Java8中推出了Optional类以及Lambda表达式,这使我们可以方便地进行代码的编写。将这两者结合,才是最常见的使用方式,也是Optional类的真正威力所在:
public class Test { public static void main(String[] args) { User user = new User(); Optional.of(user).ifPresent(user1 -> user1.setAge(18)); Optional.of(user).ifPresent(System.out::println); //而不是设计为如下: if (Optional.ofNullable(user).isPresent()) { user.setAge(18); } if (Optional.ofNullable(user).isPresent()) { System.out.println(user); } } } class User { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "age=" + age + '}'; } }
lambda在Optional类中的应用才是学习Optional类应该掌握的方法。省略了if的以lambda输入为参数的链式处理,显得更加简洁和拥有更强的逻辑性和更少的出错可能。
4.Optional类案例说明:
在我们阅读了Optional
类的源代码之后,一定可以对其有个初步的认识,现在我们接着阅读关于Optional
类使用的一个例子: Company
类中有关于员工对象的链表,如以下代码所示: 需求:在company
为空的情况下也不会抛出异常,且在不为空情况下返回一个员工链表的实例对象。
public class OptionalTest2 { public static void main(String[] args) { /** * 定义员工对象,公司对象初始化。 */ Employee employee1 = new Employee("Fisherman"); Employee employee2 = null; List<Employee> listOfEmployee = Arrays.asList(employee1,employee2); Company company = new Company(null, listOfEmployee); company =null; /** * Optional类的应用 */ Optional<Company> optionalCompany = Optional.ofNullable(company); System.out.println(optionalCompany.map(theCompany->theCompany.getList()).orElseGet(()->{ System.out.println("返回一个空的链表"); return Collections.emptyList(); })); } } /** * 员工和公司两个类的定义 */ class Employee { final String name; public Employee(String name) { this.name = name; } } class Company { final String name; List<Employee> list = new ArrayList<>(); public Company(String name, List<Employee> list) { this.name = name;r this.list = list; } public List<Employee> getList() { return list; } }
控制台输出:
返回一个空的链表 []
分析: 下面代码是上述功能实现的关键:
- map方法是一个映射方法,如果optionalCompany ≠ null,那么调用此lambda方法
theCompany->theCompany.getList()
,返回含一个value==List<Employee>
的Optional对象,否则返回Optional
类的静态对象EMPTY
; - orElseGet方法则是判断调用起的
Optional
对象若为Optional
类的静态对象EMPTY
,则执行其lambda表达式,否则(返回一个空链表,并在控制台打印出此操作),直接返回Optional对象的value
域。
Optional<Company> optionalCompany = Optional.ofNullable(company); System.out.println(optionalCompany.map(theCompany->theCompany.getList()).orElseGet(()->{ System.out.println("返回一个空的链表"); return Collections.emptyList(); }));
我们之所以不建议返回指向null
的list,这是因为由此可能会产生空指针异常,并且长度为0的空list,相关操作一般进入执行语句就相当于执行完毕推出了(长度为0,直接会退出循环、if…)。空间和时间上并不大的牺牲,解决了空指针异常问题,不失为一个好的代码编写风格。