NPE问题

  • NPE问题就是,我们在开发中经常碰到的NullPointerException。

  • 下面这种写法是比较丑陋的,为了变得优雅。

  • JAVA8提供了Optional类来优化这种写法

    1
    2
    3
    4
    5
    6
    7
    8
    user.getAddress().getProvince();
    // 在user为null时,是有可能报NullPointerException异常的,于是采用下面的写法
    if(user!=null){
    Address address = user.getAddress();
    if(address!=null){
    String province = address.getProvince();
    }
    }

Optional类型

  • Optional<T> 对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。

    • Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空
  • Optional<T>类型被当作一种更安全的方式,用来替代类型T的引用,这种引用要么引用某个对象,要么为null。

  • Optional 类的引入很好的解决空指针异常。

创建Optional值

  • API如下

    API 修饰符 NullPointerException
    optional(T value) private 构造函数,不能由外部调用的
    empty() public 产生一个空的Optional,null
    of(T value) public 产生一个具有给定值的Optional
    ofNullable(T value) public 产生一个具有给定值的Optional 不会
  • 源码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public final class Optional<T> {

    //省略....
    private static final Optional<?> EMPTY = new Optional<>();
    private Optional() {
    this.value = null;
    }

    // 产生一个具有给定值的Optional。如果value为null,那么会抛出NullPointerException异常
    public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
    }

    // 产生一个具有给定值的Optional。如果value为null,那么会产生一个空Optional
    public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
    }

    // 产生一个空的Optional
    public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
    }
    }
  • 例如:

    1
    2
    3
    public static Optional<Double> inverse(Double x){
    return x == 0 ? Optional.empty() : Optional.of(1 / x);
    }
  • ofNullable方法被用来作为可能出现的null值和可选值之间的桥梁。

    • 该方法会在obj不为null的情况下返回Optional.of(obj),否则返回Optional.empty()

获取Optional的值

  • get方法会在Optional值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个NoSuchElementEception对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public final class Optional<T> {

    //产生这个Optional的值,或者在该Optional为空时,抛出一个NoSuchElementException异常
    public T get() {
    if (value == null) {
    throw new NoSuchElementException("No value present");
    }
    return value;
    }
    }
  • 如果没有正确地设置Optional值,那么相比以前得到“null”的方式,直接抛出异常显然不是很好。

    1
    2
    3
    4
    5
    6
    Optional<T> optionalValue = ...;
    optionalValue.get().someMethod();

    //并不比下面的方式更安全
    T value = ...;
    value.someMehtod();
  • 结合判断值使用,代码结构依然丑陋

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if(optionalValue.isPresent()){
    optionalValue.get().someMethod();
    }

    //并不比下面的方式更容易处理

    if(value != null){
    value.someMethod();
    }
  • 推荐写法

    1
    2
    3
    4
    5
    6
    7
    8
    if(user!=null){
    dosomething(user);
    }

    Optional.ofNullable(user)
    .ifPresent(u->{
    dosomething(u);
    });

使用Optional值

  • API如下

    API
    orElse(T other) value值为null时,给予一个默认值 值不为null时,orElse函数依然会执行
    orElseGet(Supplier<? extends T> other) value值为null时,给予一个默认值 值不为null时,orElseGet函数并不会执行
    orElseThrow(Supplier<? extends X> exceptionSupplier) value值为null时,抛一个异常
    map(Function<? super T, ? extends U> mapper) 转换值
    flatMap(Function<? super T, Optional> mapper) 转换值
  • 源码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public final class Optional<T> {

    //产生这个Optional的值,或者在该Optional为空时,产生other
    public T orElse(T other) {
    return value != null ? value : other;
    }

    //产生这个Optional的值,或者在该Optional为空时,产生调用other的结果
    public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
    }

    //产生将该Optional的值传递给mapper后的结果,只要这个Optional不为空且结果不为null,
    //否则产生一个空Optional
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
    return value;
    } else {
    throw exceptionSupplier.get();
    }
    }

    //产生将该Optional的值传递给mapper后的结果,只要这个Optional不为空且结果不为null,否则产生空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));
    }
    }

    //产生将mapper应用于当前Optional值所产生的结果,或者在当前Optional为空时,返回一个空Optional
    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));
    }
    }
    }
  • 有效使用Optional的关键是要使用这样的方法:

    • 它在值不存在的情况下会产生一个可替代物,而只有在值存在的情况下才会使用这个值。
    1
    2
    3
    4
    5
    6
    7
    8
    //有值使产生string,无值时产生空字符串""
    String result = optionalString.orElse("");

    //值不存在的时候调用这个lambda表达式,计算默认值
    String result = optionalString.orElseGet(() -> Local.getDefault().getDisplayName());

    //在没有任何值时抛出异常
    String result = optionalString.orElseThrow(IllegalStateException::new);
  • 使用可选值的策略是只有其存在的情况下才消费该值

    • ifPresent方法会接受一个函数。如果该可选值存在,那么它会被传递给该函数。否则,不发生任何事情。

      1
      2
      3
      4
      //如果值存在时添加到某个集合中
      optionaValue.ifPresent(v -> results.add(v));
      //或者这样用,lambda表达式的方法引用
      optionaValue.ifPresent(results::add);
    • 当调用ifPresent时,从该函数不会返回任何值。如果想要处理函数的结果,应该使用map

      1
      2
      Optional<Boolean> added = optionalValue.map(results::add);
      //若值存在,Optional为处理结果true或者false,若不存在,则为空Optional

map&flatMap具体用法

  • 这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function<? super T, ? extends U>,而flapMap的入参类型为Function<? super T, Optional<U>>

    • flapMap 能处理值是Optional类型的数据
  • 如果User结构是下面这样的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class User {
    private String name;
    public String getName() {
    return name;
    }
    }

    // 这时候取name的写法如下所示
    String name = Optional.ofNullable(user).map(u-> u.getName()).get();
  • 对于flatMap而言:如果User结构是下面这样的

    • 如果想获取user的城市名称,普通类型是可以直接 user.getAddress().getCity()的方式获取
    • 使用Optional后,user.getAddress()的类型为Optional<Address>,而不是Address。
    • 为了解决这个问题,我们可以调用flatMap方法获取到Address
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class User {
    private String name;
    private Optional<Address> address;
    public Optional<String> getName() {
    return Optional.ofNullable(name);
    }
    }

    public class Address {
    private String city;
    public Optional<String> getCity() {
    return Optional.ofNullable(city);
    }
    }


    // 这时候取city值的写法如下所示
    String city = user.getAddress().flatMap(u -> u.getCity()).get();
    city = Optional.ofNullable(user).flatMap(User::getAddress).get().getCity();

判断Optional值

  • API

    API
    isPresent() 判断value值是否不为空
    ifPresent(Consumer<? super T> consumer) 在value值不为空时,做一些操作
  • 源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public final class Optional<T> {
    // 判断value值是否不为空
    public boolean isPresent() {
    return value != null;
    }

    // 如果该Optional不为空,那么就将它的值传递给consumer
    public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
    consumer.accept(value);
    }
    }
  • 使用

    1
    2
    3
    Optional.ofNullable(user).ifPresent(u->{
    // TODO: do something
    });

过滤Optional值

  • filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public final class Optional<T> {
    public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!this.isPresent()) {
    return this;
    } else {
    return predicate.test(this.value) ? this : empty();
    }
    }
    }
  • 使用

    1
    2
    3
    // user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。
    Optional<User> user1 = Optional.ofNullable(user)
    .filter(u -> u.getName().length()<6);

实战使用

从对象取值

  • 以前写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public String getCity(User user)  throws Exception{
    if(user!=null){
    if(user.getAddress()!=null){
    Address address = user.getAddress();
    if(address.getCity()!=null){
    return address.getCity();
    }
    }
    }
    throw new Excpetion("取值错误");
    }
  • JAVA8写法

    1
    2
    3
    4
    5
    6
    public String getCity(User user)  throws Exception{
    return Optional.ofNullable(user)
    .map(u-> u.getAddress())
    .map(a->a.getCity())
    .orElseThrow(()->new Exception("取指错误"));
    }

判不为空

  • 以前写法

    1
    2
    3
    if(user!=null){
    dosomething(user);
    }
  • JAVA8写法

    1
    2
    3
    4
    Optional.ofNullable(user)
    .ifPresent(u->{
    dosomething(u);
    });

值判断

  • 以前写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public User getUser(User user) throws Exception{
    if(user!=null){
    String name = user.getName();
    if("zhangsan".equals(name)){
    return user;
    }
    }else{
    user = new User();
    user.setName("zhangsan");
    return user;
    }
    }
  • java8写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public User getUser(User user) {
    return Optional.ofNullable(user)
    .filter(u->"zhangsan".equals(u.getName()))
    .orElseGet(()-> {
    User user1 = new User();
    user1.setName("zhangsan");
    return user1;
    });
    }