关于java8:Java中NullPointerException的完美解决方案

6次阅读

共计 4356 个字符,预计需要花费 11 分钟才能阅读完成。

null 在 Java 中带来的麻烦

我置信所有的 Java 程序猿肯定都遇到过 NullPointerException,空指针在 Java 程序中是最常见的,也是最烦人的;它让咱们很多程序猿产生了积重难返的感觉,所有可能产生空指针的中央都的加上if-else 查看,然而这带给咱们很多麻烦

  • Java 自身是强类型的,然而 null 毁坏了这个规定,它能够被赋值给任何对象
  • Java 的设计是让程序猿对指针无感知,然而 null 指针是个例外
  • 它会是代码变得很臃肿,到处都充斥着 if-else 的空查看,甚至是多层嵌套,代码可读性降落
  • null 自身毫无意义,示意不了

前两点不须要特地的阐明,后两点举个例子来阐明一下:
如果一个人领有一个手机,每个手机都有生成厂商,每个厂商都会有个名字,用类示意的话:

public class Person {
    private Phone phone;

    public Phone getPhone() {return phone;}
}

public class Phone {
    private Producer producer;

    public Producer getProducer() {return producer;}
}

public class Producer {
    private String name;

    public String getName() {return name;}
}

在这个例子中,如果咱们须要取到手机生成厂商的名字

public String getPhoneProducerName(Person person) {return person.getPhone().getProducer().getName();
}

因为不肯定每个人都会有一个手机,所有在调用 getProducer() 时可能会呈现NullPointerException

一门设计语言原本就是来形容世界的,在这个事例中有的人有手机,有的人也可能没有手机,所以在调用 person.getPhone() 返回的值就应该蕴含有和无这两种状况,当初通过返回 null 来示意无,然而在调用 getProducer() 却又会抛出异样,这样就不太合乎事实逻辑;所以把 null 来用来示意 不适合

在遇到这种状况通常的做法是做 null 查看, 甚至是每个中央可能产生 null 指针的做查看。

public String getPhoneProducerName(Person person) {if (person.getPhone() == null) {return "无名字";}
    if (person.getPhone().getProducer() == null) {return "无名字";}
    return person.getPhone().getProducer().getName();}

这里我曾经试图在缩小代码的层级,如果应用的是if-else,代码的层级会更深,代码可读性降落。


Optional 的简略介绍

吐槽了那么多现状的不好,当初能够祭出咱们的解决方案了 Optional;千呼万唤始进去,犹抱琵琶半遮面;那 Optional 到底是个什么货色,咱们一起来逐渐解开它的面纱。

Optional自身只是对对象的简略包装,如果对象为空,那么会构建一个空的 Optional;这样一来Optional 就蕴含了存在和不存在两个状况, 接下来能够看下下面的例子改过之后

public class Person {
    private Optional<Phone> phone;

    public Optional<Phone> getPhone() {return phone;}
}

public class Phone {
    private Producer producer;

    public Producer getProducer() {return producer;}
}

public class Producer {
    private String name;

    public String getName() {return name;}
}

因为有的人可能没有手机,有的人有,所以 Phone 须要用 Optional 包装起来;手机自身肯定会有生产的厂商,厂商肯定会有一个名字,所以这两个不须要用 Optional 包装起来。这里咱们会发现应用了 Optional 会丰盛代码的语义,让代码更加合乎事实。

而当咱们在调用 phone.getProducer().getName() 的时候不须要做 null 指针的查看,如果说在这里产生了NullPointerException,阐明这里数据自身是有问题的,不合乎事实,就应该让问题裸露进去,而不是像下面的代码一样把问题覆盖。


Optional 的罕用办法应用

1. Optional 的创立办法
Optional<Person> empty = Optional.empty();  // 申明一个空的 Optional
Optional<Person> person = Optional.of(new Person()); // 包装 Person
Optional<Person> person2 = Optional.of(null); // 不容许的操作,传入 null 会抛出空指针异样
Optional<Person> optionalPerson = Optional.ofNullable(null); // 容许传 null, 返回一个空的 Optional
2. Optional 值的获取形式
  • map、flatMap

首先咱们从新定义一下 Phone 类,除了有生产厂商之外,还有个型号;

public class Phone {
    private String model;
    private Producer producer;

    public Producer getProducer() {return producer;}
    public String getModel() {return model;}
}

当咱们须要获取到手机的型号的时候能够这样:

Optional<Phone> optionalPhone = Optional.of(new Phone());
Optional<String> model = optionalPhone.map(Phone::getModel);

当咱们须要通过 Person 对象获取到 Phone 的型号是,会想到这样:

Optional<Person> optionalPerson = Optional.of(new Person());
optionalPerson.map(Person::getPhone).map(Phone::getModel);

当咱们写进去的时候发现编译器不能通过。是因为 Person::getPhone 返回的是一个 Optional<Phone>,调用optionalPerson.map(Person::getPhone) 返回的就是 Optional<Optional<Phone>>,所以再.map 的就无奈拿到手机型号,那如何可能让返回的后果不是 Optional<Optional<Phone>>,而是Optional<Phone> 呢?

这里须要用到另一个办法 flatMapflatMapmap的区别,我在刚开始学习的时候,看到了网上的各种解释都很绕,看的很晕,最初间接关上源码来看,发现实现很简略,很容易了解,来看下源码:

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));
    }
}
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {return Objects.requireNonNull(mapper.apply(value));
    }
}

map办法在返回的时候会包装一层 OptionalflatMap 在返回的时候间接把函数的返回值返回了,函数的后果必须是 Optional;那么在后面的例子中咱们间接调用flatMap 返回的后果就是Optional<Phone>

Optional<Person> optionalPerson = Optional.of(new Person());
optionalPerson.flatMap(Person::getPhone).map(Phone::getModel);
  • 取出 Optional 中的值对象:get、orElse、orElseGet、orElseThrow、ifPresent
  1. get() : 当你明确晓得 Optional 中有值的话能够间接调用该办法,当 Optional 中没有值是该办法会抛出异样NoSuchElementException; 所以当如果存在空值的话倡议就不要调用该办法,因为这样和做 null 查看就没有区别了
  2. orElse(T other) : 提供一个默认值,当值不存在是返回这个默认值
  3. orElseGet(Supplier<? extends T> other) : 当值不存在的时候会调用 supper 函数,如果说返回这个默认值的逻辑较多,那么调用这个办法比拟适合;
  4. orElseThrow(Supplier<? extends X> exceptionSupplier) : 当值为空时会抛出一个自定义的异样
  5. ifPresent(Consumer<? super T> consumer) : 当值不为空是会调用 consumer 函数,如果值为空,那么这个办法什么都不做
  • filter 过滤出满足条件的对象

如果咱们须要过滤出手机型号 IOS 的手机,并打印出型号,代码如下:

Person person = new Person(Optional.of(new Phone("IOS")));
        Optional<Person> optionalPerson = Optional.of(person);
        optionalPerson.flatMap(Person::getPhone)
                .filter(phone -> "IOS".equals(phone.getModel()))
                .map(Phone::getModel)
                .ifPresent(System.out::println);

总结

  1. 咱们探讨了 null 在 Java 程序的问题
  2. 介绍 Java8 中引入了 Optional 来示意有和无的状况以及初始化的形式
  3. 举例说明了 Optional 中常常应用到的办法

自己菜鸟,如果有任何写的不对的中央,欢送在评论区指出

正文完
 0