极客Go实战训练营全新升级第5期2022最新含源码ppt

#1

download:极客Go实战训练营全新升级第5期2022最新含源码ppt

编程猿自学it java python go c
应用Optional处理空指针异常

背景引见

Java 8 引入了一个非常适用的 Optional 类,它主要是为理解决空指针异常(NullPointerException)。当我们对对象的属性停止检查,判别它的值能否为希冀的格式,最终却发现我们查看的并不是一个对象,而是一个空指针,它会立刻抛出一个让人腻烦的 NullPointerException 异常。

实质上,Optional 类是一个包含有可选值的包装类,这意味着 Optional 类既能够含有对象也能够为空。

案例

从一个简单的用例开端。在 Java 8 之前,任何访问对象办法或属性的调用都可能招致 NullPointerException:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

复制代码

为了确保不触发异常,需求在访问每一个值之前对其停止明白地检查:

if (user != null) {

Address address = user.getAddress();

if (address != null) {

    Country country = address.getCountry();

    if (country != null) {

        String isocode = country.getIsocode();

        if (isocode != null) {

            isocode = isocode.toUpperCase();

        }

    }

}

}

复制代码

为了简化这个过程,我们运用 Optional 优化这些代码。

根本用法解析

Optional 的对象可能包含值,也可能为空。能够运用同名办法创立一个空的 Optional。

Optional emptyOpt = Optional.empty();

emptyOpt.get();

复制代码

此时访问 emptyOpt 变量的值会招致 NoSuchElementException。

还能够运用 of() 和 ofNullable() 办法创立包含值的 Optioanal 实例,区别在于假如将 null 当作参数传进去 of() 会报空指针异常,所以当对象可能存在或者不存在,应该运用 ofNullable()。

Optional opt = Optional.of(user);

Optional opt = Optional.ofNullable(user);

复制代码

从 Optional 实例中取得实践值对象的办法之一是运用 get() 办法。

String name = “John”;

Optional opt = Optional.ofNullable(name);

assertEquals(“John”, opt.get());

复制代码

这个办法会在值为 null 的时分抛出异常。要防止异常,需求首先用 isPresent() 办法考证能否有值。

User user = new User("zjh@gmail.com", “1234”);

Optional opt = Optional.ofNullable(user);

assertTrue(opt.isPresent());

assertEquals(user.getEmail(), opt.get().getEmail());

复制代码

该办法除了执行检查,还承受一个 Consumer(消费者) 参数,假如对象不是空的,就对执行传入的 Lambda 表达式。

opt.ifPresent( u -> assertEquals(user.getEmail(), u.getEmail()));

复制代码

除 ifPresent() 办法外,Optional 类提供了 API 用以返回对象值,或者在对象为空的时分返回默许值。

能够运用的办法是 orElse() ,它的工作方式十分直接,假如有值则返回该值,否则返回传送给它的参数值。

User user = null;

User user2 = new User("zjh@gmail.com", “1234”);

User result = Optional.ofNullable(user).orElse(user2);

assertEquals(user2.getEmail(), result.getEmail());

复制代码

此时 user 对象为 null,result 返回 orElse() 参数 user2,假如对象初始值不是 null,那么默许值会被疏忽。

第二个同类型的 API 是 orElseGet() —— 其行为略有不同。这个办法会在有值的时分返回值,假如没有值,它会执行作为参数传入的 Supplier(供给者) 函数式接口,并将返回其执行结果:

User result = Optional.ofNullable(user).orElseGet( () -> user2);

复制代码

orElse() 和 orElseGet() 的不同之处在于当 ofNullable() 传入参数不为空时,orElse() 办法依然创立了 User 对象。与之相反,orElseGet() 办法不创立 User 对象。

在执行较密集的调用时,比方调用 Web 效劳或数据查询,这个差别会对性能产生严重影响。

除了 orElse() 和 orElseGet() 办法,Optional 还定义了 orElseThrow() API —— 它会在对象为空的时分抛出异常,而不是返回备选的值:

User result = Optional.ofNullable(user)

.orElseThrow( () -> new IllegalArgumentException());

复制代码

这里,假如 user 值为 null,会抛出 IllegalArgumentException。

这个办法让我们有更丰厚的语义,能够决议抛出什么样的异常,而不总是抛出 NullPointerException。

转换与过滤

经过 map() 和 flatMap() 办法能够转换 Optional 的值。

User user = new User("zjh@gmail.com", “1234”);

String email = Optional.ofNullable(user)

.map(u -> u.getEmail()).orElse("default@gmail.com");

assertEquals(email, user.getEmail());

复制代码

map() 对值应用(调用)作为参数的函数,然后将返回的值包装在 Optional 中。这就使对返回值停止链式调用的操作成为可能 —— 这里的下一环就是 orElse() 。

flatMap() 也需求函数作为参数,并对值调用这个函数,然后直接返回结果

除了转换值之外,Optional 类也提供了按条件“过滤”值的办法。

filter() 承受一个 Predicate 参数,返回测试结果为 true 的值。假如测试结果为 false,会返回一个空的 Optional。

User user = new User("zjh@gmail.com", “1234”);

Optional result = Optional.ofNullable(user)

.filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

assertTrue(result.isPresent());

复制代码

案例优化

运用 Optional 重写最早引见的案例,删除 null 检查,交换为 Optional 的办法。

User user = new User("zjh@gmail.com", “1234”);

String result = Optional.ofNullable(user)

.map(u -> u.getAddress())

.map(a -> a.getCountry())

.map(c -> c.getIsocode())

.orElse("default");

assertEquals(result, “default”);

复制代码

源码解析

private Optional() {

this.value = null;

}

private Optional(T value) {

this.value = Objects.requireNonNull(value);

}

复制代码

能够看到源码中的结构函数全部都被私有化, 我们可以经过工厂办法的方式来获取 Optional 包装类的实例。

public static Optional of(T value) {

return new Optional<>(value);

}

public static Optional ofNullable(T value) {

return value == null ? empty() : of(value);

}

public static Optional empty() {

@SuppressWarnings("unchecked")

Optional t = (Optional) EMPTY;

return t;

}

复制代码

上面的办法是不允许传 null 值的, 下面的是能够的,下面的办法传了空值, 它会默许的帮你创立一个空的 Optional 包装类对象。

isPresent() 是判别这个包装类能否为空, get() 是获取到被包装的对象。isPresent() 还能够承受一个 Consumer(消费者) 参数,假如对象不是空的,就对执行传入的 Lambda 表达式

public boolean isPresent() {

return value != null;

}

public void ifPresent(Consumer<? super T> consumer) {

if (value != null)

    consumer.accept(value);

}

public T get() {

if (value == null) {

    throw new NoSuchElementException("No value present");

}

return value;

}

复制代码

以下三个办法大致上类似, 假如被包装类为空, 一个是直接返回一个新的被包装对象, 一个是经过函数式编程接口 Supplier 返回一个新的被包装类对象, 最后一个直接返回一个指定异常。

public T orElse(T other) {

return value != null ? value : other;

}

public T orElseGet(Supplier<? extends T> other) {

return value != null ? value : other.get();

}

public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {

if (value != null) {

    return value;

} else {

    throw exceptionSupplier.get();

}

}

复制代码

以下三个办法承受 Predicate 接口,完成转换与过滤操作。

public Optional filter(Predicate<? super T> predicate) {

Objects.requireNonNull(predicate);

if (!isPresent())

    return this;

else

    return predicate.test(value) ? this : empty();

}

public Optional map(Function<? super T, ? extends U> mapper) {

Objects.requireNonNull(mapper);

if (!isPresent())

    return empty();

else {

    return Optional.ofNullable(mapper.apply(value));

}

}

public Optional flatMap(Function<? super T, Optional> mapper) {

Objects.requireNonNull(mapper);

if (!isPresent())

    return empty();

else {

    return Objects.requireNonNull(mapper.apply(value));

}

}

复制代码

总结

Optional 是 Java 言语的有益补充 —— 它旨在减少代码中的 NullPointerExceptions。

经过设计,自然的融入了 Java 8 函数式支持。

总的来说,这个简单而强大的类有助于创立简单、可读性更强、比对应程序错误更少的程序。