Java使用Optional避免NPE

本文详细介绍了Java中的Optional类,通过多个实用场景展示了如何利用ofNullable、empty、equals、filter、flatMap、map、get、hashCode、ifPresent、orElse、orElseGet、orElseThrow和toString等方法来避免NullPointerException,提高代码的健壮性和可读性。通过Optional,开发者可以更方便地处理可能为null的对象,简化了嵌套对象和Map的空值检查流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

实用场景1

测试场景2 :ofNullable

测试场景3 :empty

测试场景4 :equals

测试场景5 :filter

测试场景6 :flatMap & map

测试场景7 :get

测试场景8 :hashCode

测试场景9 :ifPresent & isPresent

测试场景10 :orElse

测试场景11 :orElseGet

测试场景12 :orElseThrow

测试场景13 :toString


        Java中多层对象取值时,为了避免NPE(NullPointerException) ,需要使用object!=null来进行判断,如果遇到使用场景A.getB().getC()Map.get("k1").get("k2")等类似的情况,层层判空使人头痛(不推荐使用try-catchtry-catch本意是捕获异常,性能差,不应该作用于正常的逻辑中)。

实用场景1

import java.util.Optional;
​
//为A.getB().getC()做准备
class A{
    B b = new B() ;
    public B getB() {
        return b;
    }
}
class B{
    C c = new C() ;
    public C getC() {
        return c;
    }
}
class C{
    int intVal = 1;
    public int getIntVal() {
        return intVal;
    }
};
​
public class OptionalTest {
    public static void main(String[] args) {
        //A.getB().getC();
        //如果不使用Optional,正常我们如果希望有值就取,无值跳过,不报NPE需要
        A a = new A();
        if(a.getB()!=null){
            B b = a.getB();
            if(b.getC()!=null){
                C c = b.getC();
                System.out.println(c.getIntVal());
            }
        }
        //使用Optional
        Optional.ofNullable(a)//判断a是否为空,如果为空返回空Optional不抛异常,否则继续
                .map(aa->aa.getB())//a映射为aa
                .map(bb->bb.getC())//aa.getB()映射为bb,类似于 bb = aa.getB();
                .ifPresent(cc->System.out.println(cc.getIntVal()));//判断如果最终取出C类实例(cc=bb.getC())如果存在,就输出cc的值,全程不抛异常,不用显示判空,有值输出,无值忽略。
        /**
        *可以将A/B/C类的任意实例赋值为null测试,不会抛异常,注意此场景适用“有值就取,无值就跳过”,不会抛异常!!
        *对于嵌套map与嵌套类相似,这里就不再举例
        */
    }
}

        场景1是笔者认为最实用的场景,没有之一。下面将一一说明Optional的所有方法使用场景,读者可以根据自己的实际情况单独使用或组合使用。首先准备测试类如下:


public class Person {
    private String name;
    private int age ;
    private int sex;
    private Optional<Person> parents;//该字段为了解释说明map方法和flatMap方法的区别,如果是该类型字段,不能使用map方法(会多包一层Optional<Optional<Person>>),
    public Person(String name, int age,  int sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
​
    public Optional<Person> getParents() {
        return parents == null ? Optional.empty() : parents;
    }
​
    public void setParents(Optional<Person> parents) {
        this.parents = parents;
    }
​
    public String toString() {
        return "name=" + name + " , age=" + age + " , sex=" + sex + "{" + parents + "}";
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getSex() {
        return sex;
    }
​
    public void setSex(int sex) {
        this.sex = sex;
    }
}

测试场景2 :ofNullable

ofNullable(obj)of(obj)方法的区别是前者对象为空返回空的Optional,后者抛异常。

@Test
    public void ofNullable_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);//空对象返回空Optional不抛异常
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
​
        System.out.println("personOptional0: " + personOptional0);
        System.out.println("personOptional1: " + personOptional1);
        // Optional.of()方法就不再举例,读者自行测试,一测就懂
    }
//    输出结果:
//    personOptional0: Optional.empty
//    personOptional1: Optional[name=p1 , age=10 , sex=1]

与之相关的源码如下:

public final class Optional<T> {
  /**
   * Optional任何方法产生的空集都返回这个集合
   * 因此任何Optional实例调用 empty()或ofNullable(null)方法后相互再使用equals(Object obj)方法比较都会返回true
   */
  private static final Optional<?> EMPTY = new Optional<>();
​
  private final T value;
​
  private Optional() {
    this.value = null;
  }
​
  /**
   * 该方法返回单例EMPTY,再次强调,注意如果你的Optional最终是空的,都引用的该实例,所有的空Optional相互比较都是true
   */
  public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
  }
​
  /**
   *该构造强制参数不能为空,注意Objects.requireNonNull方法,也是判断类不为空的一种方法(空则抛异常),读者也可以使用。
   */
  private Optional(T value) {
    this.value = Objects.requireNonNull(value);
  }
​
  /**
   *看到这里就知道为什么of方法抛异常啦
   */
  public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
  }
  /**
   *看到这里就知道为什么ofNullable方法不抛异常啦,空value返回单例EMPTY.
   */
  public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
  }
}

测试场景3 :empty

 @Test
    public void empty_Method_Test() {
​
        Person p0 = null;
​
        Optional<Person> personOptional0 = Optional.empty();//方式一
        System.out.println("personOptional0: " + personOptional0);
        personOptional0 = Optional.ofNullable(p0);//方式二
        System.out.println("personOptional0: " + personOptional0);
        //没有意外的话,方式一和二得到的是同一个Optional对象,如果比较应该返回true,读者自行测试,这里不再测试,源码上文已经提到,不再赘述。
    }
//    输出结果
//    personOptional0: Optional.empty
//    personOptional0: Optional.empty

测试场景4 :equals

@Test
    public void equals_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
        Optional<Person> personOptional3 = Optional.ofNullable(p1);
        Optional<Person> personOptional4 = Optional.ofNullable(p0);
        //
        System.out.println(personOptional0.equals(personOptional0));//true
        System.out.println(personOptional0.equals(personOptional4));//true
        System.out.println(personOptional0.equals(Optional.empty()));//true
        System.out.println(personOptional1.equals(personOptional2));//false
        System.out.println(personOptional1.equals(personOptional3));//true
​
    }

涉及到的源码如下,常规覆写

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

测试场景5 :filter

如果做数据统计和筛选相当有用的一个方法。

 @Test
    public void filter_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
        
        personOptional0.filter(person -> person.getAge() > 10)//条件筛选
            .filter(person -> person.getSex() == 2)//条件筛选
            .ifPresent(person -> System.out.println(person));//如果满足条件输出
       
        personOptional1.filter(person -> person.getAge() > 10)
            .filter(person -> person.getSex() == 2)
            .ifPresent(person -> System.out.println(person));
        
        personOptional2.filter(person -> person.getAge() > 10)
            .filter(person -> person.getSex() == 2)
            .ifPresent(person -> System.out.println(person));
        //优点就是,如果在流式的某一个条件不满足,不用担心下面逻辑是否空异常,非常方便
    }
//    name=p2 , age=20 , sex=2

相关源码:

  /**
   * 条件筛选时如果T为空不会报错,可以连续调用
   */
  public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
      return this;
    else
      return predicate.test(value) ? this : empty();
  }

测试场景6 :flatMap & map

@Test
    public void flatMap_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
        
        personOptional0.flatMap(Person::getParents) //parents实例是Optional<Person>不是Person类型,使用map会报错,此时就需要使用flatMap
                .map(Person::getName)//普通字段使用map映射,函数式相当于a->a.getName(),不熟悉可以不用,自行了解
                .map(String::trim) // String str = p.getName().trim();
                .filter(str -> !"".equals(str)) //条件筛选 null对象会自动过滤,但是""不会,需要我们自己写过滤条件
                .ifPresent(person -> System.out.println(person));//如果存在输出
​
        personOptional1.flatMap(Person::getParents)
                .map(Person::getName)
                .map(String::trim)
                .filter(str -> !"".equals(str))
                .ifPresent(person -> System.out.println(person));
​
        personOptional2.flatMap(Person::getParents)
                .map(Person::getName)
                .map(String::trim)
                .filter(str -> !"".equals(str))
                .ifPresent(person -> System.out.println(personOptional2.get()));
​
    }
//    输出结果:
//    name=p2 , age=20 , sex=2{Optional[name=p1 , age=10 , sex=1{null}]}

相关源码:注意,参数类型不一样哦(似乎明白了,又不完全明白)。

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

测试场景7 :get

顾名思义,取出对象,需要注意的是如果对象为空,会抛异常!

test@Test
    public void get_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
//        System.out.println(personOptional0.get()); 抛异常
        System.out.println(personOptional1.get());
        System.out.println(personOptional2.get());
    }
//    输出结果:
//    name=p1 , age=10 , sex=1{null}
//    name=p2 , age=20 , sex=2{Optional[name=p1 , age=10 , sex=1{null}]}

相关源码:

/*
 * 对象为空直接抛异常:值不存在
 */
public T get() {
    if (value == null) {
      throw new NoSuchElementException("No value present");
    }
    return value;
  }

测试场景8 :hashCode

这是一个众所周知的覆写方法。

@Test
    public void hashCode_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);//po2
        Optional<Person> personOptional3 = Optional.ofNullable(p2);//po3
        System.out.println(personOptional0.hashCode());
        System.out.println(personOptional1.hashCode());
        System.out.println(personOptional2.hashCode());//spo2
        System.out.println(personOptional3.hashCode());//spo3
        // po2和po3传同一个对象,所以spo2和spo3输出相同的hashCode
    }
    // equals 和hashCode方法调用的都是类的方法
//    0
//            136936250
//            593687897
//            593687897

相关源码:

  @Override
  public int hashCode() {
    return Objects.hashCode(value);
  }

测试场景9 :ifPresent & isPresent

顾名思义,ifPresent 就是对象如果存在,接下来要做... 相当于if(满足条件){ // TODO 要做的事情};isPresent就是单纯的判断,返回布尔值。

@Test
    public void ifPresent_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
        
        personOptional0.ifPresent(person -> System.out.println(person));
        personOptional1.ifPresent(person -> System.out.println(person));
        personOptional2.ifPresent(person -> System.out.println(person));
        
        System.out.println(personOptional0.isPresent()); //false
        System.out.println(personOptional1.isPresent()); //true
        System.out.println(personOptional2.isPresent()); //true
    }

相关源码:

public boolean isPresent() {
    return value != null;
  }
​
  public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
      consumer.accept(value);
  }

测试场景10 :orElse

顾名思义,如果Optional值为空就赋一个指定值

@Test
    public void orElse_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
​
        System.out.println(personOptional0.orElse(new Person("other",43,1)));
        System.out.println(personOptional1.orElse(new Person("other",43,1)));
        System.out.println(personOptional2.orElse(new Person("other",43,1)));
​
    }
//    name=other , age=43 , sex=1{null}
//    name=p1 , age=10 , sex=1{null}
//    name=p2 , age=20 , sex=2{Optional[name=p1 , age=10 , sex=1{null}]}

相关源码:

  //该方法仅仅是赋值而已...
  public T orElse(T other) {
    return value != null ? value : other;
  }

测试场景11 :orElseGet

感觉仅仅就是 orElse 之后 get一下 ...

@Test
    public void orElseGet_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
      
        System.out.println(personOptional0.orElseGet(()->new Person("other",11,3)));
        System.out.println(personOptional1.orElseGet(()->new Person("other",11,3)));
        System.out.println(personOptional2.orElseGet(()->new Person("other",11,3)));
    }

相关源码:

  // 果然跟我想的一样,就是 orElse 后 get 了一下 ...
  public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
  }

测试场景12 :orElseThrow

顾名思义,如果值为空就抛异常。

@Test
    public void orElseThrow_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
        try{
            System.out.println(personOptional0.orElseThrow(()-> new NullPointerException("obj is null")));
        }catch (NullPointerException e){
            e.printStackTrace();
        }
        try{
            System.out.println(personOptional1.orElseThrow(()-> new NullPointerException("obj is null")));
        }catch (NullPointerException e){
            e.printStackTrace();
        }
        try{
            System.out.println(personOptional2.orElseThrow(()-> new NullPointerException("obj is null")));
        }catch (NullPointerException e){
            e.printStackTrace();
        }
​
    }
//    java.lang.NullPointerException: obj is null
//    at com.xy.optional.OptionalClassTest.lambda$orElseThrow_Method_Test$21(OptionalClassTest.java:274)
//    at java.util.Optional.orElseThrow(Optional.java:290)
//    at com.xy.optional.OptionalClassTest.orElseThrow_Method_Test(OptionalClassTest.java:274)
//    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//      ...
//    name=p1 , age=10 , sex=1{null}
//    name=p2 , age=20 , sex=2{Optional[name=p1 , age=10 , sex=1{null}]}

相关源码:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
      return value;
    } else {
      throw exceptionSupplier.get();
    }
  }

测试场景13 :toString

不言

@Test
    public void toString_Method_Test() {
​
        Person p0 = null;
        Person p1 = new Person("p1", 10, 1);
        Person p2 = new Person("p2", 20, 2);
        p2.setParents(Optional.of(p1));
​
        Optional<Person> personOptional0 = Optional.ofNullable(p0);
        Optional<Person> personOptional1 = Optional.ofNullable(p1);
        Optional<Person> personOptional2 = Optional.ofNullable(p2);
​
        System.out.println(personOptional0.toString());
        System.out.println(personOptional1.toString());
        System.out.println(personOptional2.toString());
    }
//    Optional.empty
//    Optional[name=p1 , age=10 , sex=1{null}]
//    Optional[name=p2 , age=20 , sex=2{Optional[name=p1 , age=10 , sex=1{null}]}]

不语

  @Override
  public String toString() {
    return value != null
      ? String.format("Optional[%s]", value)
      : "Optional.empty";
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_evenif

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值