>>分享Java编程技术,对《Java面向对象编程》等书籍提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 16988 个阅读者 刷新本主题
 * 贴子主题:  Java Optional 解决空指针异常总结 回复文章 点赞(0)  收藏  
作者:javathinker    发表时间:2020-11-24 01:53:17     消息  查看  搜索  好友  复制  引用

  

1、概述

  • Java8的版本,新增了Optional和[Lambda]表达式,Optional主要用于作为返回类型(主要解决的问题是臭名昭著的空指针异常(NullPointerException)),并将其与流(或返回可选的方法)相结合以构建连贯API。
  • 但是,有些情况可以被认为是陷阱,因为它们会降低代码的质量,甚至导致意想不到的错误。总结以下26个例子,以避免这些陷阱。

2、 目 录

[第1项:决不将Null分配给可选变量]

     [第2项:调用Optional.get()之前,确保Optional具有值]

     [第3项:当不存在任何值时,通过Optional.orElse()方法设置/返回已经构造的默认对象]

     [第4项:不存在任何值时,通过Optional.orElseGet()方法设置/返回不存在的默认对象]

     [第5项:当不存在任何值时,自Java 10起通过orElseThrow()抛出java.util.NoSuchElementException异常]

     [第6项:当不存在任何值时,通过orElseThrow(Supplier <?extended X> exceptionSupplier)引发显式异常]

     [第7项:当你有可选的并且需要空引用时,请使用orElse(null)]

     [第8项:使用可选(如果存在)。如果不存在,则什么也不做。这是Optional.ifPresent()的工作。]

     [第9项:使用可选(如果存在)。如果不存在,请执行基于空的操作。这是Optional.ifPresentElse(),Java 9的工作。]

     [第10项:当值存在时,设置/返回该可选项。如果不存在任何值,则设置/返回另一个可选项。这是Java 9的Optional.or()的工作。]

     [第11项:Optional.orElse / orElseXXX是Lambdas中的isPresent()-get()完美替代]

     [第12项:避免仅出于获取价值的目的而链接可选方法]

     [第13项:不要声明任何类型的可选字段]

     [第14项:在构造函数参数中不要使用可选]

     [第15项:在Setters参数中不要使用可选]

     [第16项:在方法参数中不要使用可选]

     [第17项:不要使用Optional 的返回空的集合或数组]

     [第18项:避免在集合中使用Optional]

     [第19项:不要混淆Optional.of()和Optional.ofNullable()]

     [第20项:避免使用可选的,并选择非通用的OptionalInt,OptionalLong或OptionalDouble]

     [第21项:无需包装主张平等的任择方案]

     [第22项:通过Map()和flatMap()转换值]

     [第23项:使用filter()根据预定义的规则拒绝包装值]

     [第24项:我们是否需要将可选API与流API链接在一起?]

     [第25项:避免对可选选项使用身份敏感的操作]

     [第26项:如果Optional为空,则返回一个布尔值。首选Java 11,Optional.isEmpty()]

第1项:决不将Null分配给可选变量

避免:

// 避免

public Optional<Cart> fetchCart() {

    Optional<Cart> emptyCart = null;

    ...

}

首选:

// 首选

public Optional<Cart> fetchCart() {

    Optional<Cart> emptyCart = Optional.empty();

    ...

}

首选 Optional.empty()初始化  Optional而不是 null值。 Optional只是一个容器/盒子,用初始化它是没有意义的 null。

第2项:调用Optional.get()之前,确保Optional具有值

如果出于某种原因你决定使用 Optional.get(),那么请不要忘记必须 Optional在此调用之前证明该值存在。通常,将通过基于该 Optional.isPresent()方法添加检查(条件)来执行此操作。 Optional.get()在某些时候容易被弃用。

     避免:

// 避免

Optional<Cart> cart = ... ; // 这很容易是空的

...

// 如果“cart”是空的,那么这段代码将抛出一个java.util.NoSuchElementException

Cart myCart = cart.get();

首选:

// 首选

if (cart.isPresent()) {

    Cart myCart = cart.get();

    ...

} else {

    ... // 做一些不调用carter .get()的事情

}

第3项:当不存在任何值时,通过Optional.orElse()方法设置/返回已经构造的默认对象

使用Optional.orElse()方法代表了用于设置/返回值的isPresent()-get()对的优雅替代。这里重要的一点是,即使是非空可选的,也要计算oforElse()参数。这意味着即使不使用它,也会对它进行评估,这是一种性能损失。在这个上下文中,useorElse()只在参数(默认对象)已经构造完成时才使用。在其他情况下,请依赖第4项。

     避免:

// 避免

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()方法设置/返回不存在的默认对象

使用theOptional.orElseGet()方法表示另一种用于设置/返回值的替代theisPresent()-get()对的优雅方法。这里重要的一点是,参数oforElseGet()是Java 8新特性。这意味着作为参数传递的Suppliermethod只在Optionalvalue不存在时执行。因此,这有助于避免创建对象和执行在出现Optionalvalue时不需要的代码时的orElse()性能损失。

     避免:

// 避免

public String computeStatus() {

    ... // 一些用来计算状态的代码

}

public String findUserStatus(long id) {

    Optional<String> status = ... ; // 容易返回一个空的 Optional

    if (status.isPresent()) {

        return status.get();

    } else {

        return computeStatus();

    }

}

另外,请避免:

// 避免

public String computeStatus() {

    ... // 一些用来计算状态的代码

}

public String findUserStatus(long id) {

    Optional<String> status = ... ; // 容易返回一个空的 Optional

    // 即使“status”不是空的,也会调用computeStatus()

    return status.orElse(computeStatus());

}

首选:

public String computeStatus() {

    ... // some code used to compute status

}

public String findUserStatus(long id) {

    Optional<String> status = ... ; // 容易返回一个空的 Optional

    // 仅当“status”为空时才调用computeStatus()

    return status.orElseGet(this::computeStatus);

}

第5项:当不存在任何值时,自Java 10起通过orElseThrow()抛出java.util.NoSuchElementException异常

使用该 Optional.orElseThrow()方法代表了该方法的另一种优雅替代方法 isPresent()-get()。有时,当 Optional不存在值时,你要做的就是抛出 java.util.NoSuchElementException异常。从Java 10开始,这可以通过 orElseThrow()不带参数的方法来完成。对于Java 8和9,请考虑第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 <?extended X> exceptionSupplier)引发显式异常

在Java 10中,对于   java.util.NoSuchElementException,请使用   orElseThrow(),如第5项所示。使用该 Optional.orElseThrow(Supplier<? extends X> exceptionSupplier)方法代表了该方法的另一种优雅替代方法 isPresent()-get()。有时,当 Optional不存在值时,你要做的就是抛出一个明确的异常。从Java 10开始,如果该异常 java.util.NoSuchElementException仅依赖于 orElseThrow()不带参数的方法-第5项。

     避免:

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)

如果你有 Optional需要提供 null参考的,请使用   orElse(null)。否则,请避免   orElse(null)。 orElse(null)当我们有一个 Optional,并且需要 null在某些情况下调用一个接受引用的方法时,就会发生典型的使用情况。例如,让我们看一下 Method.invoke()Java Reflection API。此方法的第一个参数是要在其上调用此特定方法的对象实例。如果方法是 static,第一个参数,则应为 null。

     避免:

Method myMethod = ... ;

...

// 包含MyClass的实例,如果“myMethod”是静态的,则为空

Optional<MyClass> instanceMyClass = ... ;

...

if (instanceMyClass.isPresent()) {

    myMethod.invoke(instanceMyClass.get(), ...);  

} else {

    myMethod.invoke(null, ...);  

}

首选:

Method myMethod = ... ;

...

// 包含MyClass的实例,如果“myMethod”是静态的,则为空

Optional<MyClass> instanceMyClass = ... ;

...

myMethod.invoke(instanceMyClass.orElse(null), ...);

第8项:使用可选(如果存在)。如果不存在,则什么也不做。这是Optional.ifPresent()的工作。

当你只需要使用值时,Optional.ifPresent()是isPresent()-get()paid的不错选择。 如果没有值,则不执行任何操作。

     避免:

Optional<String> status = ... ;

...

if (status.isPresent()) {

    System.out.println("Status: " + status.get());

}

首选:

Optional<String> status ... ;

...

status.ifPresent(System.out::println);

第9项:使用可选(如果存在)。如果不存在,请执行基于空的操作。这是Optional.ifPresentElse(),Java 9的工作。

从Java 9开始,它 Optional.ifPresentOrElse()是 isPresent()-get()配对的不错选择。这与 ifPresent()方法类似, 只不过它也覆盖 else分支。

     避免:

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项:当值存在时,设置/返回该可选项。如果不存在任何值,则设置/返回另一个可选项。这是Java 9的Optional.or()的工作。

有时,对于非空,  Optional,我们想设置/返回那个  Optional。当我们 Optional为空时,我们想执行其他操作,该操作也返回一个  Optional。该 orElse()和 orElseGet()方法不能做到这一点,因为两者都将返回 展开值。现在该介绍Java 9  Optional.or()方法了,该方法能够返回 Optional描述值。否则,它将返回 Optional提供功能产生的结果。

     避免:

public Optional<String> fetchStatus() {

    Optional<String> status = ... ;

    Optional<String> defaultStatus = Optional.of("PENDING");

    if (status.isPresent()) {

        return status;

    } else {

        return defaultStatus;

    }  

}

另外,请避免:

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);

    // 或者,不定义“defaultStatus”

    return status.or(() -> Optional.of("PENDING"));

}

第11项:Optional.orElse / orElseXXX是Lambdas中的isPresent()-get()完美替代

这可用于获取链接的lambda,而不是被破坏的代码。考虑  ifPresent() 和  ifPresentOrElse() Java中9或  or() Java中9的方法,也是如此。某些特定于lambda的操作正在返回 Optional(例如, findFirst(), findAny(), reduce(),...)。尝试 Optional通过该 isPresent()-get()对使用此方法是一种笨拙的方法,因为它可能需要您打破lambda链,用 if语句污染代码,并考虑继续使用链。在这种情况下, orElse()and和 orElseXXX()非常方便,因为它们可以直接在lambda表达式链中链接,并避免破坏代码。

     例子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";

}

另外,请避免:

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,并且到处都可以看到用例。在的情况下  Optional,常见的情况是出于获得价值的单一目的而链接其方法。避免这种做法,而是依靠简单明了的代码。

     避免:

public String fetchStatus() {

    String status = ... ;

    return Optional.ofNullable(status).orElse("PENDING");

}

首选:

public String fetchStatus() {

    String status = ... ;

    return status == null ? "PENDING" : status;

}

第13项:不要声明任何类型的可选字段

请勿  Optional 在方法(包括  setters)或构造函数参数中使用。

     请记住,这 Optional 并不是要用于字段,并且不会实现 Serializable。该 Optional班是明确不打算用作一个Java Bean的属性。

     避免:

public class Customer {

    [url=] [static] [final] Optional<String> zip;

    [url=] [static] [final] Optional<String> zip = Optional.empty();

    ...

}

首选:

public class Customer {

    [url=] [static] [final] String zip;

    [url=] [static] [final] String zip = "";

    ...

}

第14项:在构造函数参数中不要使用可选

请勿将其  Optional 用作字段或方法(包括   setters)的参数。

     这是违背 Optional意图的另一种用法。  Optional用另一种抽象级别包装对象,在这种情况下,只需添加额外的样板代码。

     避免:

public class Customer {

    private final String name;               // 不可能为null

    private final Optional<String> postcode; //可能为null

    public Customer(String name, Optional<String> postcode) {

        this.name = Objects.requireNonNull(name, () -> "名称不能为空");

        this.postcode = postcode;

    }

    public Optional<String> getPostcode() {

        return postcode;

    }

    ...

}

首选:

public class Customer {

    private final String name;    

    private final String postcode;

    public Cart(String name, String postcode) {

        this.name = Objects.requireNonNull(name, () -> "名称不能为空");

        this.postcode = postcode;

    }

    public Optional<String> getPostcode() {

        return Optional.ofNullable(postcode);

    }

    ...

}

如你所见,getter现在返回  Optional。不要以这个示例作为转换所有此类getter的规则。多数情况下,getter返回集合或数组,在这种情况下,他们更喜欢返回空集合/数组而不是  Optional。
  我认为,通常将其用作获取方法的返回值肯定会过度使用。

第15项:在Setters参数中不要使用可选

  Optional 不能用作Java Bean的属性或持久属性类型。  Optional 不是 Serializable。 Optional在二传手中使用是另一种反模式。通常,我看到它用作持久属性类型(将实体属性映射为  Optional)。但是,可以 Optional在域模型实体中使用,如以下示例所示。

     避免:

@Entity

public class Customer implements Serializable {

    private static final long serialVersionUID = 1L;

    ...

    @Column(name="customer_zip")

    private Optional<String> postcode; // optional 字段, 因此可能为空

     public Optional<String> getPostcode() {

       return postcode;

     }

     public void setPostcode(Optional<String> postcode) {

       this.postcode = postcode;

     }

     ...

}

首选:

@Entity

public class Customer implements Serializable {

    private static final long serialVersionUID = 1L;

    ...

    @Column(name="customer_zip")

    private String postcode;

    public Optional<String> getPostcode() {

      return Optional.ofNullable(postcode);

    }

    public void setPostcode(String postcode) {

       this.postcode = postcode;

    }

    ...

}

第16项:在方法参数中不要使用可选

不要  Optional 用作字段或设置器和构造函数的参数。 Optional在in方法中使用参数是另一个常见错误。这将导致不必要的复杂代码。负责检查参数,而不是强制调用站点创建  Optional。这种做法会使代码混乱,并可能导致依赖性。随着时间的流逝,您将在所有地方使用它。请记住,这 Optional只是另一个对象(容器),而且价它消耗的内存是强引用的4倍!但是,你可以用,视情况而定。

     避免:

public void renderCustomer(Cart cart, Optional<Renderer> renderer,

Optional<String> name) {    

    if (cart == null) {

        throw new IllegalArgumentException("Cart不能为空");

    }

    Renderer customerRenderer = renderer.orElseThrow(

        () -> new IllegalArgumentException("Renderer 不能为空")

    );    

    String customerName = name.orElseGet(() -> "anonymous");

    ...

}

//调用这个是错误的

renderCustomer(cart, Optional.<Renderer>of(CoolRenderer::new), Optional.empty());

首选:

public void renderCustomer(Cart cart, Renderer renderer, String name) {

    if (cart == null) {

        throw new IllegalArgumentException("Cart 不能为空");

    }

    if (renderer == null) {

        throw new IllegalArgumentException("Renderer 不能为空");

    }

    String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");

    ...

}

// 调用这个方法

renderCustomer(cart, new CoolRenderer(), null);

另外,更喜欢(依靠 NullPointerException):

public void renderCustomer(Cart cart, Renderer renderer, String name) {

    Objects.requireNonNull(cart, "Cart 不能为空");        

    Objects.requireNonNull(renderer, "Renderer 不能为空");        

    String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");

    ...

}

// 调用这个方法

renderCustomer(cart, new CoolRenderer(), null);

另外,更喜欢(避免 NullPointerException和使用 IllegalArgumentException或其他异常):

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");

    ...

}

// 调用这个方法

renderCustomer(cart, new CoolRenderer(), null)

第17项:不要使用Optional 的返回空的集合或数组

支持返回一个空的集合/数组。考虑到这一点,依靠   Collections.emptyList(),   emptyMap()和 emptySet()。

     为了保持代码的清洁,轻便避免返回 Optional的 null或空的集合/数组。最好返回一个空数组或集合。

     避免:

public Optional<List<String>> fetchCartItems(long id) {

    Cart cart = ... ;    

    List<String> items = cart.getItems(); //这个可能会返回null

    return Optional.ofNullable(items);

}

首选:

public List<String> fetchCartItems(long id) {

    Cart cart = ... ;    

    List<String> items = cart.getItems(); // 这个可能会返回null

    return items == null ? Collections.emptyList() : items;

}

第18项:避免在集合中使用Optional

见示例

     避免:

Map<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("找不到这把钥匙");

} else {

    String unwrappedItem = item.orElse("NOT FOUND");

    System.out.println("Key found, Item: " + unwrappedItem);

}

首选(Java 8):

Map<String, String> items = new HashMap<>();

items.put("I1", "Shoes");

items.put("I2", null);

...

// get an item

String item = get(items, "I1");  // Shoes

String item = get(items, "I2");  // null

String item = get(items, "I3");  // NOT FOUND

private static String get(Map<String, String> map, String key) {

  return map.getOrDefault(key, "NOT FOUND");

}

还可以依赖其他方法,例如:
  •   containsKey()方法
  • 通过扩展实现简单 HashMap
  • Java 8  computeIfAbsent()方法
  • Apache Commons DefaultedMap
通过将此示例外推到其他集合,我们可以得出结论,总有比 Optional在集合中使用更好的解决方案。

     而且,甚至更糟:

Map<Optional<String>, String> items = new HashMap<>();

Map<Optional<String>, Optional<String>> items = new HashMap<>();

第19项:不要混淆Optional.of()和Optional.ofNullable()

混淆或错误地使用  Optional.of而不是 Optional.ofNullable,反之亦然,可能会导致令人不快的问题。作为此处的关键,请记住 Optional.of(null)会抛出 NullPointerException,而 Optional.ofNullable(null)会导致  Optional.empty。

     使用 Optional.of代替 Optional.ofNullable示例

     避免:

// AVOID

public Optional<String> fetchItemName(long id) {

    String itemName = ... ; // 这可能导致null

    ...

    return Optional.of(itemName); // 如果“itemName”为空,则抛出NPE :(

}

首选:

public Optional<String> fetchItemName(long id) {

    String itemName = ... ; // 这可能导致null

    ...

    return Optional.ofNullable(itemName); // 没有NPE风险  

}

使用 Optional.ofNullable代替 Optional.of示例

     避免:

      return Optional.ofNullable("PENDING"); // ofNullable不会增加任何值

首选:

      return Optional.of("PENDING"); // 没有 NPE 风险

第20项:避免使用可选的<T>,并选择非通用的OptionalInt,OptionalLong或OptionalDouble

除非你有盒装原语,避免特定需求   Optional<  T  > ,并选择非通用   OptionalInt,  OptionalLong或 OptionalDouble。

     装箱和拆箱是昂贵的操作,容易导致性能下降。为了消除这种风险,我们可以依靠  OptionalInt, OptionalLong 和 OptionalDouble。这些是基本类型的包装 int, long,和 double。

     避免使用(仅在需要装箱的原语时使用):

Optional<Integer> price = Optional.of(50);

Optional<Long> price = Optional.of(50L);

Optional<Double> price = Optional.of(50.43d);

首选:

OptionalInt price = OptionalInt.of(50);           // 打开通过 getAsInt()

OptionalLong price = OptionalLong.of(50L);        // 打开通过 getAsLong()

OptionalDouble price = OptionalDouble.of(50.43d); // 打开通过 getAsDouble()

第21项:无需包装主张平等的任择方案

有两个 Optionals在 assertEquals()不需要解开值。这是适用的,因为 Optional#equals()比较包装的值,而不是 Optional对象。

      Optional.equals()源代码:

@Override

public boolean equals(Object obj) {

    if (this == obj) {

        return true;

    }

    if (!(obj instanceof Optional)) {

        return false;

    }

    Optional<?> other = (Optional<?>) obj;

    return Objects.equals(value, other.value);

}

避免:

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()转换值

的 Optional.map()和 Optional.flatMap()是用于将非常方便的办法 Optional值。该 map()方法将函数参数应用于值,然后返回包装在中的结果  Optional,而通过比较,该 flatMap()方法采用应用于值的函数参数, Optional然后直接返回结果。

使用  map()

例子1

     避免:

Optional<String> lowername ...; // 可能是空的

// transform name to upper case

Optional<String> uppername;

if (lowername.isPresent()) {

    uppername = Optional.of(lowername.get().toUpperCase());

} else {

    uppername = Optional.empty();

}

首选:

Optional<String> lowername ...; // 可能是空的

// 将名称转换为大写

Optional<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()返回一个非空字符串

public 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()返回一个字符串

public 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  Optional

public 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 Optional

public Optional<String> getName() {

    return Optional.ofNullable(name);

}

第23项:使用filter()根据预定义的规则拒绝包装值

传递谓词(条件)作为参数并获取一个  Optional 对象。 Optional 如果满足条件,则使用首字母缩写;如果不满足条件,则使用空白 字母  Optional 。使用 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项:我们是否需要将可选API与流API链接在一起?

如果是这样,则我们使用该  Optional.stream() 方法。从Java 9开始,我们可以通过应用方法将 Optional实例视为。当您需要将Optional API与Stream API链接在一起时,这很有用。此方法创建一个元素或一个空元素(如果不存在)。此外,我们可以使用Stream API中可用的所有方法。  Stream``Optional.stream()``Stream``Stream``Optional

     避免:

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(...);

}

实际上, Optional.stream()让我们来代替 filter()和  map()用  flatMap()。

     另外,我们可以转换 Optional为 List:

public static <T> List<T> convertOptionalToList(Optional<T> optional) {

    return optional.stream().collect(toList());

}

第25项:避免对可选选项使用身份敏感的操作

这包括引用相等(==),基于身份哈希或同步。 Optional类是一个基于价值的类作为  LocalDateTime。

     避免:

Product product = new Product();

Optional<Product> op1 = Optional.of(product);

Optional<Product> op2 = Optional.of(product);

// op1 == op2 => false, expected true

if (op1 == op2) { ...

首选:

Product product = new Product();

Optional<Product> op1 = Optional.of(product);

Optional<Product> op2 = Optional.of(product);

// op1.equals(op2) => true,expected true

if (op1.equals(op2)) { ...

第26项:如果Optional为空,则返回一个布尔值。首选Java 11,Optional.isEmpty()

从Java 11开始, true如果方法 Optional为,则可以很容易地返回a  isEmpty()。

     避免使用(Java 11+):

//  (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();
}

首选(Java 11+):

//  (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();

}

这就是总结的这26个例子,希望可以避免这些陷阱,永远不会出现:java.lang.NullPointerException



----------------------------
原文链接:https://blog.51cto.com/13288309/2552824

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-11-25 11:58:40 重新编辑]
  Java面向对象编程-->Lambda表达式
  JavaWeb开发-->使用Session(Ⅰ)
  JSP与Hibernate开发-->JPA API的高级用法
  Java网络编程-->基于MVC和RMI的分布式应用
  精通Spring-->创建综合购物网站应用
  Vue3开发-->计算属性和数据监听
  为网站代码块pre标签增加一个复制代码按钮代码
  解密Java类文件的数据结构
  搞定这24道JVM面试题,要价30k都有底气
  面向对象的七大原则,你知道几个?
  不修改源代码,动态注入Java代码的方法
  Java是如何实现自己的SPI机制的?
  Java读取大文件的高效率实现_java大文件
  JNI_Java Native Interface的用法
  Java入门实用代码:线程状态监测
  Java入门实用代码: 集合中添加元素
  Java入门实用代码:集合转数组
  Java入门实用代码:自定义异常
  通过java.net.URL类连接HTTP服务器时获取响应结果的头部信息
  Java线程实现龟兔赛跑
  JDBC使用8.0驱动包连接mysql设置时区serverTimezone
  更多...
 IPIP: 已设置保密
树形列表:   
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。