Java8 Optional使用

By | 2016年5月16日

Optional<T>是对一个T对象的封装,如果存在被封装的对象,那么get()方法会返回改对象,否则会抛出NoSuchElementException。

创建Optional<T>

// Create an empty Optional.
Optional<Object> empty = Optional.empty();
// Create with given element.
Optional<Session> session = Optional.of(new Session());
Optional<Session> session = Optional.of(aNullableSession);

使用Optinal.of(obj)时,传入的对象,不能是null,否则会抛出NullPointerException。如Optional.of(null),是错误的。

通常情况下,不确定传入的对象是否为null。此时应当用Optional.ofNullable(obj),如果obj不是null,返回封装obj的Optional对象;否则返回Optional.empty()。

普通使用Optional

直接调用get()方法返回的对象,对代码没有任何改进。

RunJob job = ...
job.run();
//Havn't improved than old usage.
Optional<RunJob> optionalJob = ...
optionalJob.get().run();

Optional的isPresent()方法可以判断封装对象是否存在,但是这个并没有简化code。

if (value != null) {
  value.someMethod();
}
// No much difference than above usage.
if (optionalValue.isPresent()) {
  optionalValue.get().someMethod();
}

正确高效得使用Optional

ifPresent(Consumer): void

当对象存在的时候,执行一段代码,没有返回值

Optional<RecalculateSession> recalculate = null;
recalculate.ifPresent(r -> {
  checkFolder();
  clearResult();
  r.run();
});

map(Function<? super T, ? extends U>): Optional<U>

当对象存在的时候,执行一段代码,并返回一个值该返回值被自动封装在Optional。如果返回的是null,则返回Optional.empty()。

Optional<RecalculateSession> recalculate = null;
Optional<Boolean> result = recalculate.map(r -> {
  try {
    r.run();
    return true;
  } catch (Exception e) {
    Logger.logMsg(Logger.INFO, "Recalculate failed");
    return false;
  }
});

flatMap(Function<? super T, Optional<U>>): Optional<U>

当对象存在的时候,执行一段代码,返回一个Optional对象。区别:map()的Lambda表达式,返回的是普通变量。flatMap()的表达式,返回的是Optional对象。

Optional<String> word = Optional.of("melodrama");
//Optional<Integer> length = word.map(w -> w.length());
Optional<Integer> length = word.flatMap(w -> Optional.of(w.length()));

orElse(T): T

当对象不存在时,返回一个默认值。

Optional<String> sessionDir = Optional.empty();
String thePath = sessionDir.orElse("A default path");

orElseGet(Supplier): T

当对象不存在时,执行一段代码,返回一个默认值。

Optional<String> sessionDir = Optional.empty();
String thePath = sessionDir.orElseGet(() -> {
  if (isVisualizationSession()) {
    return "Visualization default session";
  } else {
    return "Generic default session";
  }
});

orElseThrow(Supplier): Throwable

当对象不存在时,执行一段代码,返回一个Exception。

Optional<String> sessionDir = Optional.empty();
String thePath = sessionDir.orElseThrow(() -> {
  releaseResources();
  return new NoSuchElementException();
});

Example: 连续调用,不产生NullPointerException

class SessionUtil {
  public static Session getSession(int id) {…};
  public static SessionInfo getSessionInfo(Session session) {…};
  public static String getDescription(SessionInfo info) {…};
}

场景:给定Session Id,要获得该session的description。Java8之前的做法:

int id = 99;
Session session = SessionUtil.getSession(id);
if (session != null) {
  SessionInfo sessionInfo = SessionUtil.getSessionInfo(session);
  if (sessionInfo != null) {
    String description = SessionUtil.getDescription(sessionInfo);
    if (description != null) {
      System.out.println(description);
    }
  }
}

Java8中使用Optional的map方法,不用null check。只有每步都有返回值时,最后才会打印出来description。

Optional<Integer> sessionId = Optional.of(99);
sessionId.map(SessionUtil::getSession)
         .map(SessionUtil::getSessionInfo)
         .map(SessionUtil::getDescription)
         .ifPresent(System.out::println);

如果上述SessionUtil的返回值,都是用Optional封装的,就使用flatMap()方法。

class SessionUtil {
  public static Optional<Session> getSession(int id) {...};
  public static Optional<SessionInfo> getSessionInfo(Session session) {...};
  public static Optional<String> getDescription(SessionInfo info) {...};
}

Optional<Integer> sessionId = Optional.of(99);
sessionId.flatMap(SessionUtil::getSession)
         .flatMap(SessionUtil::getSessionInfo)
         .flatMap(SessionUtil::getDescription)
         .ifPresent(System.out::println);