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

Optional

程序员文章站 2022-03-04 11:35:23
...

回顾上午的问题

public class A {

    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

public class B {

    private String bName;
    private String bAge;

    public String getbName() {
        return bName;
    }

    public void setbName(String bName) {
        this.bName = bName;
    }

    public String getbAge() {
        return bAge;
    }

    public void setbAge(String bAge) {
        this.bAge = bAge;
    }
}
复制代码

已经存在两个类,并且互相嵌套,而且不允许修改, A 、B 两个类的结构,我们要安全的访问 A B中的值,尽可能少的使用 if 语句。

A a = null;
Optional<A> a1 = Optional.ofNullable(a);
Optional<B> b = a1.filter((t) -> t.getB() != null).map(A::getB);
// 上面一行代码的问题是, t 有可能会为 null ,从而引发 NPE
B b2 = b.get();
复制代码
Optional<A> a1 = Optional.ofNullable(a);
Optional<B> b = a1.map(A::getB);
b.get(); // 有可能会抛异常,因为如果 b 为 null,那么得到的 Optional 为 empty 创建的
b.orElse(new B()); // 没有问题,返回 new B 的对象
b.orElseGet(() -> B :: new); // 没有问题,返回 new B 的对象
b.orElseThrow(() -> YdException::new);  // 手动抛出异常
复制代码

使用 Optional 带来的变化

public class PersonNoOptional {

    private Car car;

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public static class Car{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
复制代码

上面的代码是没有使用 Optional 时候,我们经常会写的样式。会出现的问题:

  • 如果某个值为 null,立马会报出 NPE

我们的解决方式

public class OptionService {

    public void opt() {
        PersonNoOptional p = new PersonNoOptional();

        PersonNoOptional.Car car = p.getCar();
        if (car != null) {
            // ....
        }
    }
    
}
复制代码

会添加很多的 if 来进行判断,甚至还有空对象设计模式(Null Object Pattern) 来处理这一类的问题。Java 8 为我们带来了 Optional 添加新的解决方式。

public class PersonOptional {
    private Optional<PersonNoOptional.Car> car;

    public Optional<PersonNoOptional.Car> getCar() {
        return car;
    }

    public void setCar(PersonNoOptional.Car car) {
        this.car = Optional.of(car);
    }

    public static class Car {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
复制代码

Peron 有可能会没有 Car,但是每一辆 Car 都必须有 name,所以我们对 Car 使用了 Optional 包装,而 name 没有使用 Optional 的原因就在这里。

Optional 的创建

empty

创建一个空的 Optional 对象

Optional<Object> empty = Optional.empty();
复制代码

empty() 方法的实现

private static final Optional<?> EMPTY = new Optional<>();

private Optional() { this.value = null; }

public static<T> Optional<T> empty() {
  @SuppressWarnings("unchecked")
  Optional<T> t = (Optional<T>) EMPTY;
  return t;
}
复制代码

of

Optional<B> optionalB = Optional.of(new B());
复制代码

of 方法中的参数如果为 null,会发生 NPE

Optional<Object> optional = Optional.of(null);
复制代码

of() 方法的实现

// Objects.requireNonNull 的实现
public static <T> T requireNonNull(T obj) {
  if (obj == null)
    throw new NullPointerException();
  return obj;
}

private Optional(T value) {
  this.value = Objects.requireNonNull(value);
}

public static <T> Optional<T> of(T value) {
  return new Optional<>(value);
}
复制代码

ofNullable

ofNullable 允许传入的参数为 null

A a = null;
Optional<A> optonal = Optional.ofNullable(a);
复制代码

ofNullable 的实现

public static <T> Optional<T> ofNullable(T value) {
  return value == null ? empty() : of(value);
}
复制代码

#Optinal 中获取值

get

A a = optionalA.get();
复制代码

如果 Optional 容器中不存在值,会抛出异常 NoSuchElementException("No value present")

get 的实现

public T get() {
  if (value == null) {
    throw new NoSuchElementException("No value present");
  }
  return value;
}
复制代码

orElse

A a = optionalA.orElse(new A());
复制代码

如果 Optional 容器中不存在值,使用 orElse 方法中定义的值。

orElse 的实现

public T orElse(T other) {
  return value != null ? value : other;
}
复制代码

orElseGet

A a = optionalA.orElseGet(A::new);
复制代码

如果 Optional 容器中不存在值,会执行定义的函数。

orElseGet 的实现

public T orElseGet(Supplier<? extends T> other) {
  return value != null ? value : other.get();
}
复制代码

orElseThrow

A a = optionalA.orElseThrow(RuntimeException::new);
复制代码

如果 Optional 容器中不存在值,会抛出指定的异常。与 get 方法的区别是,get 方法抛出的异常为固定的,该方法可以抛出指定的异常。

orElseThrow 的实现

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
  if (value != null) {
    return value;
  } else {
    throw exceptionSupplier.get();
  }
}
复制代码

Optional 容器中的值为空时,使用了 throw 关键字。

map 和 flatMap

map

public class A {
    private B b;
    public B getB() {
        return b;
    }
}

public class B {
    private Name bName;
    private String bAge;
    public Name getbName() {
        return bName;
    }

    public void setbName(Name bName) {
        this.bName = bName;
    }

    public String getbAge() {
        return bAge;
    }

    public void setbAge(String bAge) {
        this.bAge = bAge;
    }

    public static class Name{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
复制代码

A B 两个类的结构关系是互相嵌套,我们要取出 b.Name.getName() 的值

Optional<String> aName = optionalA.map(A::getB)
  .map(B::getbName)
  .map(B.Name::getName);
System.out.println(aName.orElse("kkk"));
复制代码

flatMap

如果 B 在 A 的嵌套中,使用了 Optional 包装

public class A {

    private Optional<B> b;

    public Optional<B> getB() {
        return b;
    }
}
复制代码

再使用上面的访问,就会编译报错。

原因:

Optional<Optional<B>> optional = optionalA.map(A::getB);
复制代码

map 的返回外面被包装了一层 Optional ,想要达到上面的效果,需要拆掉一层 Optional 的包装,那么此时就可以使用 flatMap 来打散一层 Optional 的包装

String kkk = optionalA.flatMap(A::getB)
  .map(B::getbName)
  .map(B.Name::getName)
  .orElse("kkk");
复制代码

ypxh就可以顺利访问了

map 和 flatMap 的区别在于,flatMap 会进行拆包(将外面的层包装拆除)的动作,而 map 不会进行拆包

Optional 提供的其他方法

isPresent

isPresent 用于判断 Optional 容器中值是否为空(null),不为空返回会 true,空返回 false

public boolean isPresent() {
  return value != null;
}
复制代码

ifPresent

ifPresent 提供了执行函数式代码的能力,当 Optional 容器中的值不为空时,会执行传入的函数式代码。

optionalA.ifPresent(c -> System.out.println(c.getB()));
复制代码

ifPresent 的实现

public void ifPresent(Consumer<? super T> consumer) {
  if (value != null)
    consumer.accept(value);
}
复制代码

filter

通过执行传入的谓词 进行过滤,如果传入的 谓词 执行结果为 true 返回 Optional 容器本身,否则返回空容器。

public Optional<T> filter(Predicate<? super T> predicate) {
  Objects.requireNonNull(predicate);
  if (!isPresent())
    return this;
  else
    return predicate.test(value) ? this : empty();
}
复制代码