七台河信息港:使用Optional,不再头疼NPE

admin 5个月前 (05-14) 科技 39 0

前言

在 Java 语言开发中,可能大多数程序员遇到最多的异常就是 NullPointException 空指针异常了。这个当初语言的开发者“仅仅由于这样实现起来更容易”而允许空引用所带来的价值是异常凄惨的。而我们开发者不得不使用多重 if 嵌套判断来规避 NPE 或者通过多个 if 连系 return 语句来终止程序。且看一个例子

若是需要处置下面的嵌套工具,这是一个用于汽车、汽车保险的客户。

public class Person {
    private Car car;
    public Car getCar() {
        return car;
    }
}
public class Car {
    private Insurance insurance;
    public Insurance getInsurance() {
        return insurance;
    }
}
public class Insurance {
    private String name;
    public String getName() {
        return name;
    }
}

那么下面的代码会存在怎样的问题呢?

public String getCarInsuranceNames(Person person) {
    return person.getCar().getInsurance().getName();
}

没错,当这小我私家没有车 / 他的车没有上保险时,代码会抛出 NPE。或者说这小我私家根本就是 null,也会直接抛出异常。我们常见的作法就是在每次 get 方式之后,举行 if 判断,增添代码的健壮性。可是这样代码会显得十分臃肿。Java 语言的开发者们也在关注着这些问题。因此在 Java8 提供了新的 API:java.util.Optional 用来优雅的处置 null。接下来就请读者和我一起揭开 Optional 神秘的面纱吧!

PS:Optional 类提供的许多 API 连系 Lambda 表达式食用更佳,另外另有许多 API 和 Stream 流中同名 API 的头脑基本一致。因此建议读者先行领会这两个知识点,可以在我的博客 Java8新特征 标签下学习

声明:本文首发于博客园,作者:后青春期的Keats;地址:https://www.cNBlogs.com/keatsCoder/ 转载请注明,谢谢!

Optional 入门

Optional 类是一个封装 T 值的类,变量 T 存在时,Optional 只是对他做一个简朴的封装,若是 T 不存在,缺失的值会被建模成一个空工具,由 Optional.empty() 返回。下面这张图可以形象的形貌 Optional

现在我们实验着重构之前关于 人 车 保险 的代码

public class Person {
    private Optional<Car> car;
    public Optional<Car> getCar() {
        return car;
    }
}
public class Car {
    private Optional<Insurance> insurance;
    public Optional<Insurance> getInsurance() {
        return insurance;
    }
}
public class Insurance {
    private String name;
    public String getName() {
        return name;
    }
}

注重:对于保险来说,我们从逻辑层面限制每个保险公司都有名称,若是没有,那一样平常是数据出了问题而非代码的问题,开发者应该着手去寻找为什么数据库存在名字为空的保险公司。而不是这里抛出 NPE,故而我们不用将 Insurance 的 name 字段使用 Optional 包裹

通过上面的代码,我们已经将工具由 Optional 所包裹了,那接下来我们该若何使用它呢?

建立 Optional 工具

建立一个空工具

Optional<Object> empty = Optional.empty();

Optional.empty(); 该方式返回一个空工具,

凭据一个非空值建立

Optional<Car> car = Optional.of(c);

Optional.of(T t); 方式会返回一个 Optional 工具,然则需要注重 ,若是 of 方式参数是 null,该行会抛出 NPE。

允许空值建立

Optional car = Optional.ofNullABLe(c);

为了制止在建立 Optional 工具时,由于源工具为空而引发的 NPE,该类还提供了 ofNullable 方式,当参数为 null 时,返回 Optional.empty()。内部的 API 是这样的

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

map --- 从 Optional 中提取和转换值

public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent()) {
        return empty();
    } else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

Optional 类提供 map 方式,吸收一个函数式接口 Function 的实现类,若是挪用者是空的,则返回 empty(),否则对 Optional 中的工具 value 挪用 Function 实现类中的 apply() 方式,再包装成 Optional 返回。可以用下面的图直观的看到 map 执行的历程:

请注重,在 map 执行完 apply 方式拿到返回值之后,会自动将返回值再次包裹成 Optional 工具。因此我们若是根据下面的方式革新我们之前的方式,编译是无法通过的:

person.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName);

我们来剖析一下: person.map(Person::getCar) 革新后的 person 类中, getCar 方式返回 Optional 工具,而 map 又将 Optional 包装到 Optional 中,形成 Optional<Optional > emmm...套娃式包装。两层包装的 car 是无法通过 map 在挪用 getInsurance 方式的。

幸运的是,和 Stream 一样,Optional 也提供了扁平化流的方式 flatMap()。且看源码

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent()) {
        return empty();
    } else {
        @SuppressWarnings("unchecked")
        Optional<U> r = (Optional<U>) mapper.apply(value);
        return Objects.requireNonNull(r);
    }
}

flatMap() 比 map() 方式多了一个执行完后将嵌套 Optional 强转成 Optional 的操作,制止了流不能继续使用的尴尬处境。因此,我们可以将获取保险公司名称的方式革新成下面这样:

public String getCarInsuranceName(Optional<Person> person) {
    return person.flatMap(Person::getCar)
        .flatMap(Car::getInsurance)
        .map(Insurance::getName)
        .orElse("Unknown");
}

其中 orElse() 方式示意当最终 Optional 包裹的工具照样空时,返回的默认

PS:由于 Optional 并没有实现序列化接口,因此若是你的项目中使用了某些要求序列化的框架,并且在某个类中使用 Optional 包裹了字段。可能会由序列化引发程序故障。

操作 Optional 中的变量

get()

通过 get() 方式获取变量,若是变量存在就直接获得该变量,否则抛出一个 throw new NoSuchElementException("No value present"); 异常。一样平常不建议使用该方式究竟直接用 get() 方式了,还要整 Optional 这些花里胡哨的干啥呢

orElse()

在工具为 null 时提供一个默认值

orElseGet(Supplier<? extends T> other)

在工具为 null 通过挪用 supplier 提供者接口的实现,返回一个值

orElseThrow()

在工具为 null 抛出一个可定制的异常信息,可以用来抛出项目中的自定义异常,以便全局异常捕捉器抓取及响应数据

ifPresent(Consumer<? super T> action)

当工具不为 null 时,执行消费者操作。为 null 时啥也不干

更优雅的判断语句

我们经常挪用某个工具的某个方式去判断其属性。为了平安操作。首先需要对该工具举行非空校验。例如要检查保险公司名称是否为 Keats,需要这么写

if(i != null && "Keats".equals(i.getName())){
    System.out.println("yes");
}

现在我们可以这么写

Optional<Insurance> insurance = Optional.ofNullable(i);
insurance.filter(in -> "Keats".equals(in.getName())).ifPresent(in -> System.out.println("yes"));

先看 filter 的源码

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent()) {
        return this;
    } else {
        return predicate.test(value) ? this : empty();
    }
}

首先第一步检查了谓词实现非空,第二步判断 Optional 中的工具若是为空则返回空 Optional,若是不为空执行谓词方式,条件建立则返回该工具。否则返回空 Optional。即仅当 Optional 中工具不为 null 且相符条件时,返回该工具之后通过 ifPresent() 方式执行接下来的逻辑。异常利便易懂

其他

Optional 还提供了一些基础类型工具对应的类,如 OptionalInt、OptionalLong 同 Stream 流一样,接纳基本操作类型处置数据,制止了自动拆装箱带来的性能损失。但却牺牲了 map、flatMap、filter 方式。开发中需酌情使用

码字不易,若是你以为读完以后有收获,不妨点个推荐让更多的人看到吧!

,

Sunbet

www.ipvps.cn信誉来自于每一位客户的口碑,Sunbet携手江苏安腾科技有限公将致力服务好每位Sunbet会员。!

皇冠体育声明:该文看法仅代表作者自己,与本平台无关。转载请注明:七台河信息港:使用Optional,不再头疼NPE

网友评论

  • (*)

最新评论

文章归档

站点信息

  • 文章总数:533
  • 页面总数:0
  • 分类总数:8
  • 标签总数:965
  • 评论总数:158
  • 浏览总数:1743