Optional你用对了吗?(26条件建议)

Optional设计者布赖恩·戈茨说过Optional旨在为库方法返回类型提供一种有限的机制,其中需要一种明确的方式来表示"无结果",而对这种情况使用null极有可能导致错误。

那么,如何以预期的方式使用Optional呢?,通常,我们通过学习如何不使用它到学习使用它们过程,通过26的示例来优雅而轻松解决Optional的使用情况。

1、不要赋值Optional以null值

  • 避免

    • public Optional<Cart> fetchCart() {
      
          Optional<Cart> emptyCart = null;
          ...
      }
      
  • 改进 (使用Optional.empty())

    • public Optional<Cart> fetchCart() {
      
          Optional<Cart> emptyCart = Optional.empty();
          ...
      }    
      
  • 用Optional.empty()来初始化一个Optional而不是一个null值,Optional 只是一个容器或者说盒子,用空值初始化它没有任何意义的。

2、在调用Optional.get()之前确保Optional具有值

  • 避免

    • Optional<Cart> cart = ... ; // 这个很容易为空
      ...
      // 如果 "cart"为空那么这个方法将会抛出 java.util.NoSuchElementException
      Cart myCart = cart.get();
      
  • 改进(使用 isPresent()进行预先判断是否有值)

    • if (cart.isPresent()) {
          Cart myCart = cart.get();
          ... //写"myCart"一些逻辑
      } else {
          ... // 这时候就不能调用cart.get()方法
      }
      

3、当没有值存在时,通过Optional.orElse()方法设置/返回一个已经构造好的默认对象

  • 简化isPresent – get()这种方式,一行代码替换if else

  • 避免

    • public static final String USER_STATUS = "UNKNOWN";
      ...
      public String findUserStatus(long id) {
      
          Optional<String> status = ... ; // 很容易返回一个空的 Optional
      
          if (status.isPresent()) {
              return status.get();
          } else {
              return USER_STATUS;
          }
      }
      
  • 改进

    • public static final String USER_STATUS = "UNKNOWN";
      ...
      public String findUserStatus(long id) {
      
          Optional<String> status = ... ; // 很容易返回一个空的 Optional
      
          return status.orElse(USER_STATUS);
      }
      

4、当没有值存在时,通过Optional.orElseGet()方法设置/返回一个已经构造好的默认对象

  • orElse 和orElseGet区别就是,值不为空也会执行orElse代码, orElseGet不会执行(减少一次调用)

  • 避免1

    • public String computeStatus() {
          ... // 用于计算状态代码
      }
      
      public String findUserStatus(long id) {
      
          Optional<String> status = ... ; //  很容易返回一个空的 Optional
      
          if (status.isPresent()) {
              return status.get();
          } else {
              return computeStatus();
          }
      }
      
  • 避免2

    • public String computeStatus() {
          ... // 用于计算状态代码
      
      }
      
      public String findUserStatus(long id) {
      
          Optional<String> status = ... ; // 很容易返回一个空的 Optional
      
          // 即使 "status" 不为空,computeStatus()也会被调用
          return status.orElse(computeStatus()); 
      }
      
  • 改进

    • public String computeStatus() {    ... // 用于计算状态代码}public String findUserStatus(long id) {    Optional<String> status = ... ; // 很容易返回一个空的 Optional    // 仅 "status" 为空,computeStatus()会被调用    return status.orElseGet(this::computeStatus);}
      

5、当没有值存在时,从Java10开始通过orElseThrow()抛出java.util.NoSuchElementException异常

  • 如果java 8 和java9 可以考虑使用第6点

  • 避免

    • public String findUserStatus(long id) {    Optional<String> status = ... ; // 很容易返回一个空的 Optional    if (status.isPresent()) {        return status.get();    } else {        throw new NoSuchElementException();            }}
      
  • 改进

    • public String findUserStatus(long id) {    Optional<String> status = ... ; //  很容易返回一个空的 Optional    return status.orElseThrow();}
      

6、当没有值存在时,通过orElseThrow(Supplier<? extends X> exceptionsSupplier) 抛出一个显式定义异常

  • 不像第5点默认就是NoSuchElementException异常,这里需要显式定义

  • 避免

    • public String findUserStatus(long id) {    Optional<String> status = ... ; // 很容易返回一个空的 Optional    if (status.isPresent()) {        return status.get();    } else {        throw new IllegalStateException();     }}
      
  • 改进

    • public String findUserStatus(long id) {    Optional<String> status = ... ; // 很容易返回一个空的 Optional    return status.orElseThrow(IllegalStateException::new);}
      

7、当你有一个可选项并且需要一个空引用时,可以使用orElse(null)

  • 避免

    • Method myMethod = ... ;...// contains an instance of MyClass or empty if "myMethod" is staticOptional<MyClass> instanceMyClass = ... ;...if (instanceMyClass.isPresent()) {    myMethod.invoke(instanceMyClass.get(), ...);  } else {    myMethod.invoke(null, ...);  }  
      
  • 改进

    • Method myMethod = ... ;...// contains an instance of MyClass or empty if "myMethod" is staticOptional<MyClass> instanceMyClass = ... ;...myMethod.invoke(instanceMyClass.orElse(null), ...); 
      

8、使用Optional如果它存在。如果它不存在,则什么都不做。这是Optional.ifPresent()满足要求

  • 避免

    • Optional<String> status = ... ;...if (status.isPresent()) {    System.out.println("Status: " + status.get());}
      
  • 改进

    • Optional<String> status ... ;...status.ifPresent(System.out::println);  
      

9、使用Optional如果它存在和不存在情况都进行对应逻辑,java9 Optional.ifPresentElse()可以胜任

  • 避免

    • Optional<String> status = ... ;if(status.isPresent()) {    System.out.println("Status: " + status.get());} else {    System.out.println("Status not found");}
      
  • 改进

    • Optional<String> status = ... ;status.ifPresentOrElse(    System.out::println,     () -> System.out.println("Status not found"));
      

10、在java9, 当值存在时,设置/返回该可选值,当没有值存在时,设置/返回其他可选。Optional.or()可以满足要求

  • 避免1

    • public Optional<String> fetchStatus() {    Optional<String> status = ... ;    Optional<String> defaultStatus = Optional.of("PENDING");    if (status.isPresent()) {        return status;    } else {        return defaultStatus;    }  }
      
  • 避免2

    • public Optional<String> fetchStatus() {    Optional<String> status = ... ;    return status.orElseGet(() -> Optional.<String>of("PENDING"));}
      
  • 改进

    • public Optional<String> fetchStatus() {    Optional<String> status = ... ;    Optional<String> defaultStatus = Optional.of("PENDING");    return status.or(() -> defaultStatus);    // or, without defining "defaultStatus"    return status.or(() -> Optional.of("PENDING"));}
      

11、Optional.orElse/orElseXXX是Lambdas中isPresent()-get()对的完美替代品

  • 例子1

    • 避免1

      • List<Product> products = ... ;Optional<Product> product = products.stream()    .filter(p -> p.getPrice() < price)    .findFirst();if (product.isPresent()) {    return product.get().getName();} else {    return "NOT FOUND";}
        
    • 避免2

      • List<Product> products = ... ;Optional<Product> product = products.stream()    .filter(p -> p.getPrice() < price)    .findFirst();return product.map(Product::getName)    .orElse("NOT FOUND");
        
    • 改进

      • List<Product> products = ... ;return products.stream()    .filter(p -> p.getPrice() < price)    .findFirst()    .map(Product::getName)    .orElse("NOT FOUND");
        
  • 例子2

    • 避免

      • Optional<Cart> cart = ... ;Product product = ... ;...if(!cart.isPresent() ||    !cart.get().getItems().contains(product)) {    throw new NoSuchElementException();}  
        
    • 改进

      • Optional<Cart> cart = ... ;Product product = ... ;...cart.filter(c -> c.getItems().contains(product)).orElseThrow();  
        

12、避免以获取值单一目的而使用Optional的方法(滥用)

  • 避免

    • public String fetchStatus() {    String status = ... ;    return Optional.ofNullable(status).orElse("PENDING");}
      
  • 改进

    • public String fetchStatus() {    String status = ... ;    return status == null ? "PENDING" : status;}
      

13、不要声明任何类型的字段是Optional

  • 它没有实现Serializable接口,它定义目的也不是作为java Bean的字段

  • 避免

    • public class Customer {    [access_modifier] [static] [final] Optional<String> zip;    [access_modifier] [static] [final] Optional<String> zip = Optional.empty();    ...}
      
  • 改进

    • public class Customer {    [access_modifier] [static] [final] String zip;    [access_modifier] [static] [final] String zip = "";    ...}
      

14、不要在构造函数中使用Optional(自己可能为空)

  • 避免

    • public class Customer {    private final String name;               // cannot be null    private final Optional<String> postcode; // optional field, thus may be null    public Customer(String name, Optional<String> postcode) {        this.name = Objects.requireNonNull(name, () -> "Name cannot be null");        this.postcode = postcode;    }    public Optional<String> getPostcode() {        return postcode;    }    ...}
      
  • 改进

    • public class Customer {    private final String name;     // cannot be null    private final String postcode; // optional field, thus may be null    public Cart(String name, String postcode) {        this.name = Objects.requireNonNull(name, () -> "Name cannot be null");        this.postcode = postcode;    }    public Optional<String> getPostcode() {        return Optional.ofNullable(postcode);    }    ...}
      

15、不要在Setters参数使用Optional

  • 避免

    • @Entitypublic class Customer implements Serializable {    private static final long serialVersionUID = 1L;    ...    @Column(name="customer_zip")    private Optional<String> postcode; // optional field, thus may be null     public Optional<String> getPostcode() {       return postcode;     }     public void setPostcode(Optional<String> postcode) {       this.postcode = postcode;     }     ...}
      
  • 改进

    • // PREFER@Entitypublic class Customer implements Serializable {    private static final long serialVersionUID = 1L;    ...    @Column(name="customer_zip")    private String postcode; // optional field, thus may be null    public Optional<String> getPostcode() {      return Optional.ofNullable(postcode);    }    public void setPostcode(String postcode) {       this.postcode = postcode;    }    ...}
      

16、不用在方法参数中使用Optional参数

  • 避免1

    • public void renderCustomer(Cart cart, Optional<Renderer> renderer,                           Optional<String> name) {         if (cart == null) {        throw new IllegalArgumentException("Cart cannot be null");    }    Renderer customerRenderer = renderer.orElseThrow(        () -> new IllegalArgumentException("Renderer cannot be null")    );        String customerName = name.orElseGet(() -> "anonymous");     ...}// call the method - don't do thisrenderCustomer(cart, Optional.<Renderer>of(CoolRenderer::new), Optional.empty());
      
  • 改进

    • public void renderCustomer(Cart cart, Renderer renderer, String name) {    if (cart == null) {        throw new IllegalArgumentException("Cart cannot be null");    }    if (renderer == null) {        throw new IllegalArgumentException("Renderer cannot be null");    }    String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");    ...}// call this methodrenderCustomer(cart, new CoolRenderer(), null);
      
  • 改进1(判空异常)

    • public void renderCustomer(Cart cart, Renderer renderer, String name) {    Objects.requireNonNull(cart, "Cart cannot be null");            Objects.requireNonNull(renderer, "Renderer cannot be null");            String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");    ...}// call this methodrenderCustomer(cart, new CoolRenderer(), null);
      
  • 改进2 ( 不想要默认抛出空指针异常,自定义异常工具类)

    • // 自己写工具public final class MyObjects {    private MyObjects() {        throw new AssertionError("Cannot create instances for you!");    }    public static <T, X extends Throwable> T requireNotNullOrElseThrow(T obj,         Supplier<? extends X> exceptionSupplier) throws X {               if (obj != null) {            return obj;        } else {             throw exceptionSupplier.get();        }    }}public void renderCustomer(Cart cart, Renderer renderer, String name) {    MyObjects.requireNotNullOrElseThrow(cart,                 () -> new IllegalArgumentException("Cart cannot be null"));    MyObjects.requireNotNullOrElseThrow(renderer,                 () -> new IllegalArgumentException("Renderer cannot be null"));        String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");    ...}// call this methodrenderCustomer(cart, new CoolRenderer(), null); 
      

17、不要使用Optional返回空集合或数组

  • 对于集合和数组,我们要记住可以世俗Collections.emptyList(), emptyMap() 和emptySet()

  • 避免

    • public Optional<List<String>> fetchCartItems(long id) {    Cart cart = ... ;        List<String> items = cart.getItems(); // this may return null    return Optional.ofNullable(items);}
      
  • 改进

    • public List<String> fetchCartItems(long id) {    Cart cart = ... ;        List<String> items = cart.getItems(); // this may return null    return items == null ? Collections.emptyList() : items;}
      

18、避免在集合使用使用Optional

  • 避免

    • // AVOIDMap<String, Optional<String>> items = new HashMap<>();items.put("I1", Optional.ofNullable(...));items.put("I2", Optional.ofNullable(...));...Optional<String> item = items.get("I1");if (item == null) {    System.out.println("This key cannot be found");} else {    String unwrappedItem = item.orElse("NOT FOUND");    System.out.println("Key found, Item: " + unwrappedItem);}
      
  • 改进

    • Map<String, String> items = new HashMap<>();items.put("I1", "Shoes");items.put("I2", null);...// get an itemString item = get(items, "I1");  // ShoesString item = get(items, "I2");  // nullString item = get(items, "I3");  // NOT FOUNDprivate static String get(Map<String, String> map, String key) {  return map.getOrDefault(key, "NOT FOUND");}
      

19、不用混淆Optional.of()和Optional.ofNullable()方法

  • Optional.of(null)将会抛出NullPointerException, Optional.ofNullable(null) 结果将会返回Optional.empty

  • 使用Optional.of替换Optional.ofNullable例子

    • 避免

      • public Optional<String> fetchItemName(long id) {    String itemName = ... ; // this may result in null    ...    return Optional.of(itemName); // this throws NPE if "itemName" is null :(}
        
    • 改进

      • public Optional<String> fetchItemName(long id) {    String itemName = ... ; // this may result in null    ...    return Optional.ofNullable(itemName); // no risk for NPE    }
        
  • 使用Optional.ofNullable替换Optional.of例子

    • 避免

      • return Optional.ofNullable("PENDING"); 
        
    • 改进

      • return Optional.of("PENDING"); // no risk to NPE
        

20、尽量选择非泛型OptionalInt、OptionalLong或OptionalDouble,而不是Optional

  • 减少拆箱和装箱性能损耗

  • 避免

    • Optional<Integer> price = Optional.of(50);Optional<Long> price = Optional.of(50L);Optional<Double> price = Optional.of(50.43d);
      
  • 改进

    • OptionalInt price = OptionalInt.of(50);           // unwrap via getAsInt()OptionalLong price = OptionalLong.of(50L);        // unwrap via getAsLong()OptionalDouble price = OptionalDouble.of(50.43d); // unwrap via getAsDouble()
      

21、并不需要去掉Optionals去断言是否相等

  • 避免

    • Optional<String> actualItem = Optional.of("Shoes");Optional<String> expectedItem = Optional.of("Shoes");        assertEquals(expectedItem.get(), actualItem.get());
      
  • 改进

    • Optional<String> actualItem = Optional.of("Shoes");Optional<String> expectedItem = Optional.of("Shoes");        assertEquals(expectedItem, actualItem);
      

22、通过Map()和flatMap()转换值

  • 例子(使用map)

    • 避免

      • Optional<String> lowername ...; // may be empty// transform name to upper caseOptional<String> uppername;if (lowername.isPresent()) {    uppername = Optional.of(lowername.get().toUpperCase());} else {    uppername = Optional.empty();}
        
    • 改进

      • Optional<String> lowername ...; // may be empty// transform name to upper caseOptional<String> uppername = lowername.map(String::toUpperCase);
        
  • 例子2(连续使用)

    • 避免

      • List<Product> products = ... ;Optional<Product> product = products.stream()    .filter(p -> p.getPrice() < 50)    .findFirst();String name;if (product.isPresent()) {    name = product.get().getName().toUpperCase();} else {    name = "NOT FOUND";}// getName() return a non-null Stringpublic String getName() {    return name;}
        
    • 改进

      • List<Product> products = ... ;String name = products.stream()    .filter(p -> p.getPrice() < 50)    .findFirst()    .map(Product::getName)    .map(String::toUpperCase)    .orElse("NOT FOUND");// getName() return a Stringpublic String getName() {    return name;}
        
  • 使用flatMap()

    • 避免

      • List<Product> products = ... ;Optional<Product> product = products.stream()    .filter(p -> p.getPrice() < 50)    .findFirst();String name = null;if (product.isPresent()) {    name = product.get().getName().orElse("NOT FOUND").toUpperCase();}// getName() return an Optionalpublic Optional<String> getName() {    return Optional.ofNullable(name);}
        
    • 改进

      • List<Product> products = ... ;String name = products.stream()    .filter(p -> p.getPrice() < 50)    .findFirst()    .flatMap(Product::getName)    .map(String::toUpperCase)    .orElse("NOT FOUND");// getName() return an Optionalpublic Optional<String> getName() {    return Optional.ofNullable(name);}
        

23、使用filter()拒绝基于预定义规则的包装值

  • 避免

    • public boolean validatePasswordLength(User userId) {    Optional<String> password = ...; // User password    if (password.isPresent()) {        return password.get().length() > 5;    }    return false;}
      
  • 改进

    • public boolean validatePasswordLength(User userId) {    Optional<String> password = ...; // User password    return password.filter((p) -> p.length() > 5).isPresent();}
      

24、我们是否需要将Optional API与Stream API 连接起来

  • 避免

    • public List<Product> getProductList(List<String> productId) {    return productId.stream()        .map(this::fetchProductById)        .filter(Optional::isPresent)        .map(Optional::get)        .collect(toList());}public Optional<Product> fetchProductById(String id) {    return Optional.ofNullable(...);}
      
  • 改进

    • public List<Product> getProductList(List<String> productId) {    return productId.stream()        .map(this::fetchProductById)        .flatMap(Optional::stream)        .collect(toList());}public Optional<Product> fetchProductById(String id) {    return Optional.ofNullable(...);}
      

25、避免在Optional上使用敏感操作

  • == 或 同步方法

  • 避免

    • Product product = new Product();Optional<Product> op1 = Optional.of(product);Optional<Product> op2 = Optional.of(product);// op1 == op2 => false, expected trueif (op1 == op2) { ...
      
  • 改进

    • Product product = new Product();Optional<Product> op1 = Optional.of(product);Optional<Product> op2 = Optional.of(product);// op1.equals(op2) => true,expected trueif (op1.equals(op2)) { ...
      
  • 永远不要这么做

    • Optional<Product> product = Optional.of(new Product());synchronized(product) {    ...}
      

26、Java11增加了Optional.isEmpty()方法,返回一个boolean值

  • 避免

    • // AVOID (Java 11+)public Optional<String> fetchCartItems(long id) {    Cart cart = ... ; // this may be null    ...        return Optional.ofNullable(cart);}public boolean cartIsEmpty(long id) {    Optional<String> cart = fetchCartItems(id);    return !cart.isPresent();}
      
  • 改进

    • // PREFER (Java 11+)public Optional<String> fetchCartItems(long id) {    Cart cart = ... ; // this may be null    ...        return Optional.ofNullable(cart);}public boolean cartIsEmpty(long id) {    Optional<String> cart = fetchCartItems(id);    return cart.isEmpty();}
      
  • 码完了。。。

总结

  1. 尽量保证Optional本身不能为null
  2. 能不用Optional就不用Optional
  3. 尽量不使用 isPresent-get()模式,寻找替代orElseXXX方法
  4. 不用使用Optional作为同步锁对象 ,具体原因Value-based classes should not be used for locking

结尾

  • 感谢大家的耐心阅读,如有建议请私信或评论留言。
  • 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步
  • 下一篇研究一下创建SocketFrameHandler类
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值