欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Optional源码分析(未完)

程序员文章站 2022-06-07 13:12:56
...

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("[email protected]", "1234");
}

上面的代码中,两种方法都调用了 createNewUser() 方法,这个方法会记录一个消息并返回 User 对象。

代码输出如下:

Using orElse
Creating New User
Using orElseGet
Creating New User

由此可见,当对象为空而返回默认对象时,行为并无差异。

我们接下来看一个类似的示例,但这里 Optional 不为空

@Test
public void givenPresentValue_whenCompare_thenOk() {
    User user = new User("[email protected]", "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());
}