如何防止或避免NPE地狱 !

如何防止或避免NPE地狱 !

Scroll Down

<阿里Java开发手册>提到过一点"防止NPE,是程序员的基本修养

  • NPE:Null Pointer Exception, 可能是基本上是最常见的一种异常类型, 同时也是最容易忽视的异常! 实际开发中,这个问题很容易就可以发生, 一发生可能就得重新打包上线(比较常见的几个行为: 强制转换, 数据库查询返回null, Session取数据)

举例 代码

//此时如果user 为 null, 则出 空指针异常
user.getArea().getName();

改进

//但是这种写法并不优雅(if 地狱),为了避免这种情况, Java8 提供了Optional类!
if(user != null){
	user.getArea().getName();
}

解析

1. Optional(T value),empty(),of(T value),ofNullable(T value)

of(T value)

Optional(T value)是private权限, 不可外部调用.其余为public权限.故其本质为内部存储了一个真实的值, 在构造期间直接判断是否为空. 创建Optional对象,差别在于of不允许参数是null,而ofNullable则无限制。[下图源码: 摘自网络]
image.png

//of(T value) 源码
/**
     * Returns an {@code Optional} with the specified present non-null value.
     *
     * @param <T> the class of the value
     * @param value the value to be present, which must be non-null
     * @return an {@code Optional} with the value present
     * @throws NullPointerException if value is null
     */
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

也就是说of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。
    除此之外呢,Optional类内部还维护一个value为null的对象,大概就是长下面这样的
    /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
    private Optional() {
        this.value = null;
    }

    /**
     * Returns an empty {@code Optional} instance.  No value is present for this
     * Optional.
     *
     * @apiNote Though it may be tempting to do so, avoid testing if an object
     * is empty by comparing with {@code ==} against instances returned by
     * {@code Option.empty()}. There is no guarantee that it is a singleton.
     * Instead, use {@link #isPresent()}.
     *
     * @param <T> Type of the non-existent value
     * @return an empty {@code Optional}
     */
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

所以: empty()的作用就是返回EMPTY对象。

ofNullable(T value)作用

源码:

    /**
     * Returns an {@code Optional} describing the specified value, if non-null,
     * otherwise returns an empty {@code Optional}.
     *
     * @param <T> the class of the value
     * @param value the possibly-null value to describe
     * @return an {@code Optional} with a present value if the specified value
     * is non-null, otherwise an empty {@code Optional}
     */
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

2.orElse(T other)orElseGet(Supplier<? extends T> other)orElseThrow(Supplier<? extends X> exceptionSupplier)

这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

//这两个函数的区别:
//当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法
//至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示
@Test
    public void test() {
        User user = null;
        user = Optional. ofNullable (user) .orElse (createUser() ) ;
        user = Optional. ofNullable (user) .orElseGet( () -> createUser() ) ;
    }
    public User createUser() {
        User user = new User();
        user.setName("jianglong") ;
        return user;
    }

3. map(Function<? super T, ? extends U> mapper)flatMap(Function<? super T, Optional<U>> mapper)

源码:

    /**
     * If a value is present, apply the provided mapping function to it,
     * and if the result is non-null, return an {@code Optional} describing the
     * result.  Otherwise return an empty {@code Optional}.
     *
     * @apiNote This method supports post-processing on optional values, without
     * the need to explicitly check for a return status.  For example, the
     * following code traverses a stream of file names, selects one that has
     * not yet been processed, and then opens that file, returning an
     * {@code Optional<FileInputStream>}:
     *
     * <pre>{@code
     *     Optional<FileInputStream> fis =
     *         names.stream().filter(name -> !isProcessedYet(name))
     *                       .findFirst()
     *                       .map(name -> new FileInputStream(name));
     * }</pre>
     *
     * Here, {@code findFirst} returns an {@code Optional<String>}, and then
     * {@code map} returns an {@code Optional<FileInputStream>} for the desired
     * file if one exists.
     *
     * @param <U> The type of the result of the mapping function
     * @param mapper a mapping function to apply to the value, if present
     * @return an {@code Optional} describing the result of applying a mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null
     */
    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));
        }
    }

    /**
     * If a value is present, apply the provided {@code Optional}-bearing
     * mapping function to it, return that result, otherwise return an empty
     * {@code Optional}.  This method is similar to {@link #map(Function)},
     * but the provided mapper is one whose result is already an {@code Optional},
     * and if invoked, {@code flatMap} does not wrap it with an additional
     * {@code Optional}.
     *
     * @param <U> The type parameter to the {@code Optional} returned by
     * @param mapper a mapping function to apply to the value, if present
     *           the mapping function
     * @return the result of applying an {@code Optional}-bearing mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null or returns
     * a null result
     */
    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函数所接受的入参类型为Function<? super T, ? extends U>,而flapMap的入参类型为Function<? super T, Optional<U>>
在具体用法上,对于map而言:
如果User结构是下面这样的

public class User {
	private String name;
	public String getName() {
		return name;
	}
}
//取出name
String name = Optional.ofNullable(user).map(u -> u.getName()).get();

4. isPresent()ifPresent(Consumer<? super T> consumer)

isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些操作。至于ifPresent(Consumer<? super T> consumer),用法也很简单

Optional.ofNullable(user).ifPressent(u -> {
	
})

实例: 节选网络上的几个实例


Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
 
// isPresent判断值是否存在
System.out.println(optional1.isPresent() == true);
System.out.println(optional2.isPresent() == false);

以前写法:
image.png
Java8写法
image.png

以前写法:
image.png
Java8写法:
image.png

以前写法:
image.png
Java8写法:
image.png