专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

使用 Optional 类避免 NPE

空指针异常在日常开发中大家应该都遇到过,一旦没处理好,程序直接将其抛出将非常影响体验。

在阿里巴巴的 Java 开发手册中针对 NPE 给出了如下的建议:

【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
1) 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。
反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
2) 数据库的查询结果可能为 null。
3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
5) 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。
6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题。

目前我们使用的也是 JDK8,那本文就来学习下这个 Java8 中的 Optional 类,了解其基本的使用方式,并尝试改良项目中的代码。

创建一个 Optional 实例

Optional 类提供了 3 个静态方法来创建一个 Optional 实例

80_1.png

  • empty()

    直接返回一个空的 Optional

    ptional<String> emptyOpt = Optional.empty();

  • of()

    使用一个非空对象创建一个 Optional,如果传入的值是 Null, 会抛出 NPE

    Optional<String> notnullOpt = Optional.of("String");

  • ofNullable

    接受一个可以为 Null 的值创建 Optional

    Optional<String> nullableOpt = Optional.ofNullable("String");

isPresent() 与 get()

80_2.png

  • ##### isPresent() #####
    public boolean isPresent() {
        return value != null;
    }

通过 isPresent() 的实现可以它和 if(value != null) 并没有区别,如果只是单纯的想判断一个对象是否为 Null,不推荐使用这种方式,因为还需要新建一个 Optional 对象,这是无意义的。

* ##### get() #####

get() 是从一个 Optional 对象中获取值最直接最简单的方式,但是需要注意的是,如果在使用get() 时没有使用 isPresent() 进行 Null 检查,假设 Optional 为空,会抛出 NoSuchElementException。

所以,在实际使用 Optional 类的时候,是需要避免使用这个两个方法的,当代码中出现了这种写法时,应当思考下是否使用传统的方式即可,不要为了使用特性而强行使用。

常用方法介绍

Optional 类常用方法使用频率排行

1、public Optional map(Function<? super T, ? extends U> mapper)
2、public T orElse(T other)
3、public T orElseGet(Supplier<? extends T> other)
4、public void ifPresent(Consumer<? super T> consumer)
5、public Optional filter(Predicate<? super T> predicate)
6、public Optional flatMap(Function<? super T, Optional> mapper)
7、public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

  • orElse()

    当 Optional 有值时直接返回值,为 Null 时返回指定的值。

    Seller seller = new Seller("A001", "Lewis");
    Optional<Seller> sellerNotNullOpt = Optional.of(seller);
    Optional<Seller> sellerNullOpt = Optional.empty();
    Seller notNullSeller = sellerNotNullOpt.orElse(new Seller("A002", "LewisDefault"));
    Seller nullSeller = sellerNullOpt.orElse(new Seller("A002", System.out.println("orElse() 有值 -> " + notNullSeller.getSellerId());
    System.out.println("orElse() 无值 -> " + nullSeller.getSellerId());

    --output
    orElse() 有值 -> A001
    orElse() 无值 -> A002

  • orElseGet()

    当 Optional 有值时直接返回值,为 Null 时调用函数产生结果并返回。

    Seller notNullSeller = sellerNotNullOpt.orElseGet(() -> new Seller("A002", "LewisDefault"));
    Seller nullSeller = sellerNullOpt.orElseGet(() -> new Seller("A002", "LewisDefault"));
    System.out.println("orElseGet() 有值 -> " + notNullSeller.getSellerId());
    System.out.println("orElseGet() 无值 -> " + nullSeller.getSellerId());

    --output
    orElseGet() 有值 -> A001
    orElseGet() 无值 -> A002

orElseGet() 与 orElse() 的区别

orElse() 方法不管 Optional 是否为 Null 都会调用生成默认值的方法,而 orElseGet() 只会在 Optional 为空的时候调用。

* ##### orElseThrow() #####

当 Optional 有值时直接返回值,无值抛出指定的异常。
    Seller notNullSeller = sellerNotNullOpt.orElseThrow(() -> new NoSuchElementException("No value present."));
    System.out.println("orElseThrow() 有值 -> " + notNullSeller.getSellerId());
    try {
        seller nullSeller = sellerNullOpt.orElseThrow(() -> new NoSuchElementException("No value present."));
    } catch (NoSuchElementException exception) {
        System.out.println("orElseThrow() 无值 -> " + exception.getMessage());
    }
    --output
    orElseThrow() 有值 -> A001
    orElseThrow() 无值 -> No value present.

  • ifPresent()

    存在值则调用指定的函数。

    sellerNotNullOpt.ifPresent(s -> System.out.println("存在值,进行方法调用 ->" + seller.getSellerId()));
    sellerNullOpt.ifPresent(s -> System.out.println("不存在值,不进行方法调用"));

    --output
    存在值,进行方法调用 ->A001

  • map()

    判断值是否存在,存在则用该值包装一个新的 Optional 返回,值不存在则返回一个空 Optional。

    Seller seller = new Seller("A001", "Lewis", "address1");
    Optional<Seller> sellerNotNullOpt = Optional.of(seller);
    Optional<Seller> sellerNullOpt = Optional.empty();
    String notNullAddress = sellerNotNullOpt
              .map(Seller::getSellerAddress)
              .map(Address::getAddress1)
              .orElse("No address1.");
    String nullAddress = sellerNullOpt
               .map(Seller::getSellerAddress)
               .map(Address::getAddress1)
               .orElse("No address1.");

    System.out.println("notNullAddress -> " + notNullAddress);
    System.out.println("nullAddress -> " + nullAddress);

    --output
    notNullAddress -> address1
    nullAddress -> No address1.

使用 map() 可以有效的处理级联判断,比如,用传统的方式,上方获取 Address1 的代码会写成这样。
    if (seller != null) {
        Address address = seller.getSellerAddress();
        if (address != null) {
            String address1 = address.getAddress1();
            } else {
                return "No address1.";
                }
        } else {
            return "No address1.";
        }

  • flatMap()

    与 map() 方法类似,区别在于 flatMap() 是将传入的 mapper 函数执行的返回值是一个 Optional,而 map() 的 mapper 函数是返回一个具体的值, map() 方法会调用 Optional.ofNullable() 将其包装成一个 Optional。

    Seller seller = new Seller("A001", "Lewis", "address1");
    Optional<Seller> sellerNotNullOpt = Optional.of(seller);
    String address1 = sellerNotNullOpt
             .flatMap(s -> Optional.ofNullable(s.getSellerAddress()))
             .flatMap(a -> Optional.ofNullable(a.getAddress1()))
             .orElse("No address1.");
    System.out.println("flatMap address1 -> " + address1);

    --output
    flatMap address1 -> address1

  • filter()

    filter 方法接受一个 Predicate(Predicate函数式接口的主要作用就是提供一个test方法,接受一个参数返回一个布尔类型) 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。

    Seller seller = new Seller("A001", "Lewis", "address1");
    Optional<Seller> sellerOpt = Optional.of(seller);
    Optional<Seller> sellerFilterOpt = sellerOpt.filter(c -> StringUtils.equalsIgnoreCase("A001", c.getSellerId()));
    System.out.println("filter result -> " + sellerFilterOpt.map(Seller::getSellerId).orElse("No this seller."));
    --output
    filter result -> A001

文章永久链接:https://tech.souyunku.com/41256

未经允许不得转载:搜云库技术团队 » 使用 Optional 类避免 NPE

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们

© 2010-2025   搜云库技术团队   网站地图