Optional

回顧上午的問題

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 語句。java

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 的緣由就在這裏。ui

Optional 的建立

empty

建立一個空的 Optional 對象this

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,會發生 NPEspa

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 中獲取值code

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();
}
複製代碼
相關文章
相關標籤/搜索