Optional源码分析(未完)
Optional 类是Java8的新特性,Optional是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
先来看看Optional类的解释
/**
* A container object which may or may not contain a non-null value.
* If a value is present, {@code isPresent()} will return {@code true} and
* {@code get()} will return the value.
*
* <p>Additional methods that depend on the presence or absence of a contained
* value are provided, such as {@link #orElse(java.lang.Object) orElse()}
* (return a default value if value not present) and
* {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
* of code if the value is present).
*
* <p>This is a <a href="../lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code Optional} may have unpredictable results and should be avoided.
*
* @since 1.8
*/
- Optional类是一个可以或不可装载一个非空值的容器,如果当前建值不为空 isPresent()方法将返回true,同时get()方法将返回Optional对象装载的value值。
- 下面也大概介绍了一下追加的几个方法,将会在下面详细解释到。
public final class Optional<T> {
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
/**
* If non-null, the value; if null, indicates no value is present
*/
private final T value;
/**
* Constructs an empty instance.
*
* @implNote Generally only one empty instance, {@link Optional#EMPTY},
* should exist per VM.
*/
private Optional() {
this.value = null;
}
/**
* Constructs an instance with the value present.
*
* @param value the non-null value to be present
* @throws NullPointerException if value is null
*/
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
…………
}
- 首先看到Optional是一个带<T>的泛型类,这个<T>泛型在后来也会有很大的作用。
- 其次会看到有一个Optional<?> EMPTY常量,这个常量调无参构造器实例化一个Optional对象。
- 无参构造器,将成员变量value赋值为null。
下面介绍Optional的几个常用的静态方法
empty()方法
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
@SuppressWarnings注解的作用是,给编译器一条指令,告诉编译器对注解的代码元素的内部某些警告保持静默,unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。
该静态方法的泛型T都是由调用该方法的Optional的泛型类型决定,empty()方法其实很简单,将该类的常量EMPTY强制转换为相同类型的Optional对象,而这个empty()方法返回的Optional对象的value值为null。
下面是empty()方法的一些测试
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull() {
Optional<User> optionalUser = Optional.empty();
optionalUser.get();
}
//测试将会通过,因为此时的optionalUser的value值实则为null,将会抛出NoSuchElementException异常
get()方法
/**
* If a value is present in this {@code Optional}, returns the value,
* otherwise throws {@code NoSuchElementException}.
*
* @return the non-null value held by this {@code Optional}
* @throws NoSuchElementException if there is no value present
*
* @see Optional#isPresent()
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
get()方法是Optional对象用到最多的方法,因为Optional对象的value值需要使用到该方法去获取,有点像Map,但在Optional对象中,一个对象只是一个对象的容器,所以也用不着根据键去获取值。
get()方法的实现:如果该对象的value值不为null,则返回该value。 若该对象的value为null则抛出 NoSuchElementException异常。
of() 和 ofNullable()方法
通过看最开始的源码,大家可以发现,Optional类将自己的两个构造方法全部私有化,那么肯定会开放公共方法供外部访问。
of()方法
/**
* Returns an {@code Optional} with the specified present non-null value.
*
* @param <T> the class of the value
* @param value the value to be present, which must be non-null
* @return an {@code Optional} with the value present
* @throws NullPointerException if value is null
*/
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
of()方法,通过传入的参数,获取到泛型T同时也是作为Optional对象返回值的泛型。
of()方法调用有参构造器,传入T类型的value,接着可以看到有参构造器
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
同时我们也看到它调用了Objects类的requireNonNull方法
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
也就是说,使用Optional.of(T value)方法,如果传入的对象为null,则会抛出空指针异常。
下面是一些测试的代码
@Test(expected = NullPointerException.class)
public void whenCreateOfEmptyOptional_thenNullPointerException() {
User user = null;
// 使用of和ofNullable方法创建包含值的Optional。两个方法不同之处在于如果
// 将null传入作为参数,of方法会抛出空指针异常。
// 应该在对象明确不为null时使用of方法
Optional<User> opt = Optional.of(user);
// 若对象可能是null或非null则应该使用ofNullable方法
// Optional<User> opt = Optional.ofNullable(user);
}
//该方法测试也将通过,因为调用of()方法时传入的实则是null,同时将抛出NullPointerException
ofNullable()方法
/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param <T> the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*/
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
相比of()方法而言ofNullable()方法对传入null值时的处理更加优雅,可以看到,代码使用了一个三目运算符,若传入参数value为null,则调用empty方法为value赋值为null,若传入参数value不为空,则调用of()方法,因为这个时候of()方法并不会因为传入参数为null而抛出空指针异常。
下面是一些测试的代码
@Test(expected = NullPointerException.class)
public void whenCreateOfEmptyOptional_thenNullPointerException() {
User user = null;
// 使用of和ofNullable方法创建包含值的Optional。两个方法不同之处在于如果 将null传入作为参数,of方法会抛出空指针异常。
// 应该在对象明确不为null时使用of方法
// Optional<User> opt = Optional.of(user);
// 若对象可能是null或非null则应该使用ofNullable方法
Optional<User> opt = Optional.ofNullable(user);
}
//这时程序并不会通过,因为@Test内容中我们期望它抛出空指针异常,可是使用的是ofNullable()方法,已经对value为null时做了相应的处理
isPresent()方法
/**
* Return {@code true} if there is a value present, otherwise {@code false}.
*
* @return {@code true} if there is a value present, otherwise {@code false}
*/
public boolean isPresent() {
return value != null;
}
isPresent()方法对当前Optional对象的value值进行判断,若value为null则返回false,否则返回true。
这里还存在另一个isPresentf()方法
/**
* If a value is present, invoke the specified consumer with the value,
* otherwise do nothing.
*
* @param consumer block to be executed if a value is present
* @throws NullPointerException if value is present and {@code consumer} is
* null
*/
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。
Java8支持不用接口直接通过lambda表达式传入参数。
如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码:
@Test
public void whenCheckIfPresent_thenOk() {
User user = new User("zsz", 1, "12345");
// User user = null;
Optional<User> opt = Optional.ofNullable(user);
assertTrue(opt.isPresent());
// 检查是否有值的另一个选择是 ifPresent() 方法。
// 该方法除了执行检查,还接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda表达式:
opt.ifPresent( u -> assertEquals(user.getId(), u.getId()));
// assertEquals(user.getId(), opt.get().getId());
}
orElse()方法
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}
orElse()方法,它的工作方式非常直接,如果有值则返回该值,否则利用三目运算符返回传递给它的参数值。
下面是测试的代码
@Test
public void whenEmptyValue_thenReturnDefault() {
User user = null;
User user1 = new User("zsz", 1, "zsz");
// 方法 orElse(),它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值:
// 若user值不为空则忽略orElse()方法的对象
User result = Optional.ofNullable(user).orElse(user1);
assertEquals(user1.getId(), result.getId());
}
//上述代码将通过,因为user是一个null对象,所以orElse()方法会根据这个null值返回user1,也就是orElse()方法参数的值
如果对象的初始值不是 null,那么默认值会被忽略
orElseGet()方法
/**
* Return the value if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* @param other a {@code Supplier} whose result is returned if no value
* is present
* @return the value if present otherwise the result of {@code other.get()}
* @throws NullPointerException if value is not present and {@code other} is
* null
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
乍一看和orElse()方法差不多,但是他们之间存在着较大的区别
我们先来看看对象为空时他们的行为:
@Test
public void givenEmptyValue_whenCompare_thenOk() {
User user = null
logger.debug("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.debug("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
private User createNewUser() {
logger.debug("Creating New User");
return new User("extra@gmail.com", "1234");
}
上面的代码中,两种方法都调用了 createNewUser() 方法,这个方法会记录一个消息并返回 User 对象。
代码输出如下:
Using orElse
Creating New User
Using orElseGet
Creating New User
由此可见,当对象为空而返回默认对象时,行为并无差异。
我们接下来看一个类似的示例,但这里 Optional 不为空
@Test
public void givenPresentValue_whenCompare_thenOk() {
User user = new User("john@gmail.com", "1234");
logger.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
输出为
Using orElse
Creating New User
Using orElseGet
这个示例中,两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象。
与之相反,orElseGet() 方法不创建 User 对象。
在执行较密集的调用时,比如调用 Web 服务或数据查询,这个差异会对性能产生重大影响。
orElseThrow()方法
/**
* Return the contained value, if present, otherwise throw an exception
* to be created by the provided supplier.
*
* @apiNote A method reference to the exception constructor with an empty
* argument list can be used as the supplier. For example,
* {@code IllegalStateException::new}
*
* @param <X> Type of the exception to be thrown
* @param exceptionSupplier The supplier which will return the exception to
* be thrown
* @return the present value
* @throws X if there is no value present
* @throws NullPointerException if no value is present and
* {@code exceptionSupplier} is null
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
除了 orElse() 和 orElseGet() 方法,Optional 还定义了 orElseThrow() API —— 它会在对象为空的时候抛出异常,而不是返回备选的值:
@Test(expected = IllegalArgumentException.class)
public void whenThrowException_thenOk() {
User result = Optional.ofNullable(user)
.orElseThrow( () -> new IllegalArgumentException());
}
这里,如果 user 值为 null,会抛出 IllegalArgumentException。
这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException。
map()方法
/**
* If a value is present, apply the provided mapping function to it,
* and if the result is non-null, return an {@code Optional} describing the
* result. Otherwise return an empty {@code Optional}.
*
* @apiNote This method supports post-processing on optional values, without
* the need to explicitly check for a return status. For example, the
* following code traverses a stream of file names, selects one that has
* not yet been processed, and then opens that file, returning an
* {@code Optional<FileInputStream>}:
*
* <pre>{@code
* Optional<FileInputStream> fis =
* names.stream().filter(name -> !isProcessedYet(name))
* .findFirst()
* .map(name -> new FileInputStream(name));
* }</pre>
*
* Here, {@code findFirst} returns an {@code Optional<String>}, and then
* {@code map} returns an {@code Optional<FileInputStream>} for the desired
* file if one exists.
*
* @param <U> The type of the result of the mapping function
* @param mapper a mapping function to apply to the value, if present
* @return an {@code Optional} describing the result of applying a mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null
*/
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));
}
}
map()方法注释说明很多,我们来好好解释一下这个方法。
来看一个例子
@Test
public void whenMap_thenOk() {
User user = new User("zsz", 1, "zsz");
int id = Optional
.ofNullable(user)
.map(u -> u.getId())
.orElse(2);
assertEquals(id, user.getId());
}