Optional源码分析(未完)

简介: Optional源码分析(未完)Optional 类是Java8的新特性,Optional是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

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());
}
相关文章
|
15天前
|
缓存 监控 Java
ThreadLocal 源码解析get(),set(), remove()用不好容易内存泄漏
ThreadLocal 源码解析get(),set(), remove()用不好容易内存泄漏
19 1
|
Java 编译器 API
【小家Java】Lombok的使用详解(最详尽的解释,覆盖讲解所有可用注解),解决@Builder.Default默认值问题(下)
【小家Java】Lombok的使用详解(最详尽的解释,覆盖讲解所有可用注解),解决@Builder.Default默认值问题(下)
【小家Java】Lombok的使用详解(最详尽的解释,覆盖讲解所有可用注解),解决@Builder.Default默认值问题(下)
|
3月前
|
XML Java 数据格式
面试题:怎样把所有的组件的lazy-init值都设置为默认true?
面试题:怎样把所有的组件的lazy-init值都设置为默认true?
19 0
|
4月前
|
存储 前端开发 Java
Java【代码分享 13】前端动态添加一条记后端使用JDK1.8实现map对象根据key的部分值进行分组(将map对象封装成指定entity对象)
Java【代码分享 13】前端动态添加一条记后端使用JDK1.8实现map对象根据key的部分值进行分组(将map对象封装成指定entity对象)
22 0
|
4月前
|
SQL JSON Java
Java【问题记录 02】对象封装+固定排序+list All elements are null引起的异常处理+Missing artifact com.sun:tools:jar:1.8.0
Java【问题记录 02】对象封装+固定排序+list All elements are null引起的异常处理+Missing artifact com.sun:tools:jar:1.8.0
43 0
|
7月前
|
Java
Java优雅解决空指针问题源码级别刨析Optional 2
Java优雅解决空指针问题源码级别刨析Optional
41 0
|
7月前
|
安全 Java
Java优雅解决空指针问题源码级别刨析Optional 1
Java优雅解决空指针问题源码级别刨析Optional
31 0
|
11月前
|
存储 Java API
Spring5源码 - 03 普通对象对应的BeanDefinition是如何存入DefaultListableBeanFactory#beanDefinitionMap 源码分析
Spring5源码 - 03 普通对象对应的BeanDefinition是如何存入DefaultListableBeanFactory#beanDefinitionMap 源码分析
66 0
|
11月前
|
Dubbo Java 应用服务中间件
再谈序列化之rpc调用失败和jackson序列化时不允许Map中的key为null
再谈序列化之rpc调用失败和jackson序列化时不允许Map中的key为null
181 0
|
JSON fastjson 数据格式
JSONObject 项目启动时初始化fastjson的Provider,添加SerializerFeature的策略为WriteMapNullValue
JSONObject 项目启动时初始化fastjson的Provider,添加SerializerFeature的策略为WriteMapNullValue
300 0
JSONObject 项目启动时初始化fastjson的Provider,添加SerializerFeature的策略为WriteMapNullValue