null 不好,我真的推荐你使用 Optional
"Null 很糟糕." - Doug Lea。
Doug Lea 是一位美国的计算机科学家,他是 Java 平台的并发和集合框架的主要设计者之一。他在 2014 年的一篇文章中说过:“Null sucks.”1,意思是 null 很糟糕。他认为 null 是一种不明确的表示,它既可以表示一个值不存在,也可以表示一个值未知,也可以表示一个值无效。这样就会导致很多逻辑错误和空指针异常,给程序员带来很多麻烦。他建议使用 Optional 类来封装可能为空的值,从而提高代码的可读性和健壮性。
"发明 null 引用是我的十亿美元错误。" - Sir C. A. R. Hoare。
Sir C. A. R. Hoare 是一位英国的计算机科学家,他是快速排序算法、Hoare 逻辑和通信顺序进程等重要概念的发明者。他在 2009 年的一个软件会议上道歉说:“I call it my billion-dollar mistake. It was the invention of the null reference in 1965.”,意思是他把 null 引用称为他的十亿美元错误。他说他在 1965 年设计 ALGOL W 语言时,引入了 null 引用的概念,用来表示一个对象变量没有指向任何对象。他当时认为这是一个很简单和自然的想法,但后来发现这是一个非常糟糕的设计,因为它导致了无数的错误、漏洞和系统崩溃。他说他应该使用一个特殊的对象来表示空值,而不是使用 null。
自作者从事 Java 编程一来,就与 null 引用相伴,与 NullPointerException 相遇已经是家常便饭了。
null 引用是一种表示一个对象变量没有指向任何对象的方式,它是 Java 语言中的一个特殊值,也是导致空指针异常(NullPointerException)的主要原因。虽然 null 引用可以用来表示一个值不存在或未知,也可以用来节省内存空间。但是它也不符合面向对象的思想,因为它不是一个对象,不能调用任何方法或属性。
可以看到,null 引用并不好,我们应该尽量避免使用 null,那么我们该怎么避免 null 引用引起的逻辑错误和运行时异常嘞?
其实这个问题 Java 的设计者也知道,于是他们在 Java8 之后设计引入了 Optional 类解决这个问题,本文将给大家详细介绍下 Optional 类的设计目的以及使用方法。
Optional 类是什么?
Optional 类是 java 8 中引入的一个新的类,它的作用是封装一个可能为空的值,从而避免空指针异常(NullPointerException)。Optional 类可以看作是一个容器,它可以包含一个非空的值,也可以为空。Optional 类提供了一些方法,让我们可以更方便地处理可能为空的值,而不需要显式地进行空值检查或者使用 null。
推荐作者开源的 H5 商城项目waynboot-mall,这是一套全部开源的微商城项目,包含三个项目:运营后台、H5 商城前台和服务端接口。实现了商城所需的首页展示、商品分类、商品详情、商品 sku、分词搜索、购物车、结算下单、支付宝/微信支付、收单评论以及完善的后台管理等一系列功能。 技术上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中间件。分模块设计、简洁易维护,欢迎大家点个 star、关注我。
github 地址:https://github.com/wayn111/waynboot-mall
Optional 类的设计
Optional 类的设计是基于函数式编程的思想,它借鉴了 Scala 和 Haskell 等语言中的 Option 类型。Optional 类实现了 java.util.function 包中的 Supplier、Consumer、Predicate、Function 等接口,这使得它可以和 lambda 表达式或者方法引用一起使用,形成更简洁和优雅的代码。
Optional 类被 final 修饰,因此它是一个不可变的类,它有两个静态方法用于创建 Optional 对象。
Optional.empty()
Optional.empty 表示一个空的 Optional 对象,它不包含任何值。
// 创建一个空的 Optional 对象
Optional<String> empty = Optional.empty();
Optional.of(T value)
Optional.of 表示一个非空的 Optional 对象,它包含一个非空的值。
// 创建一个非空的 Optional 对象
Optional<String> hello = Optional.of("Hello");
Optional.ofNullable(T value)
注意,如果我们使用 Optional.of 方法传入一个 null 值,会抛出 NullPointerException。如果我们不确定一个值是否为空,可以使用 Optional.ofNullable 方法,它会根据值是否为空,返回一个相应的 Optional 对象。例如:
// 创建一个可能为空的 Optional 对象
Optional<String> name = Optional.ofNullable("Hello");
Optional 对象的使用方法
Optional 对象提供了一些方法,让我们可以更方便地处理可能为空的值,而不需要显式地进行空值检查或者使用 null。以下是一些常用的方法。
isPresent()
判断 Optional 对象是否包含一个非空的值,返回一个布尔值。
get()
如果 Optional 对象包含一个非空的值,返回该值,否则抛出 NoSuchElementException 异常。
// 使用 isPresent 和 get 方法
Optional<String> name = Optional.ofNullable("tom");
if (name.isPresent()) {
System.out.println("Hello, " + name.get());
} else {
System.out.println("Name is not available");
}
// 输出:Hello tom
ifPresent(Consumer<? super T> action)
如果 Optional 对象包含一个非空的值,执行给定的消费者操作,否则什么也不做。
// 使用 ifPresent(Consumer<? super T> action)
Optional<String> name = Optional.ofNullable("tom");
name.ifPresent(s -> {
System.out.println("Hello, " + name.get());
});
// 输出:Hello tom
orElse(T other)
如果 Optional 对象包含一个非空的值,返回该值,否则返回给定的默认值。
// 使用 orElse(T other)
Optional<String> name = Optional.ofNullable(null);
String greeting = "Hello, " + name.orElse("Guest");
System.out.println(greeting);
// 输出:Hello Guest
orElseGet(Supplier<? extends T> supplier)
如果 Optional 对象包含一个非空的值,返回该值,否则返回由给定的供应者操作生成的值。
// 使用 orElseGet(Supplier<? extends T> supplier)
Optional<String> name = Optional.ofNullable(null);
String greeting = "Hello, " + name.orElseGet(() -> "Guset");
System.out.println(greeting);
// 输出:Hello Guset
orElseThrow(Supplier<? extends X> exceptionSupplier)
如果 Optional 对象包含一个非空的值,返回该值,否则抛出由给定的异常供应者操作生成的异常。
// 使用 orElseThrow(Supplier<? extends X> exceptionSupplier)
Optional<String> name = Optional.ofNullable(null);
String greeting = "Hello, " + name.orElseThrow(() -> new NullPointerException("null"));
// 抛出 java.lang.NullPointerException: null 异常
map(Function<? super T,? extends U> mapper)
如果 Optional 对象包含一个非空的值,对该值应用给定的映射函数,返回一个包含映射结果的 Optional 对象,否则返回一个空的 Optional 对象。
// 使用 map(Function<? super T,? extends U> mapper)
Optional<String> name = Optional.ofNullable("tom");
String greeting = "Hello, " + name.map(s -> s.toUpperCase()).get();
System.out.println(greeting);
// 输出:Hello TOM
flatMap(Function<? super T,Optional<U>> mapper)
如果 Optional 对象包含一个非空的值,对该值进行 mapper 参数操作,返回新的 Optional 对象,否则返回一个空的 Optional 对象。
// 使用 flatMap(Function<? super T,Optional<U>> mapper)
Optional<String> name = Optional.ofNullable("tom");
String greeting = name.flatMap(s -> Optional.of("Hello " + s)).get();
System.out.println(greeting);
// 输出:Hello tom
filter(Predicate<? super T> predicate)
如果 Optional 对象包含一个非空的值,并且该值满足给定的谓词条件,返回包含该值的 Optional 对象,否则返回一个空的 Optional 对象。
// filter(Predicate<? super T> predicate)
Optional<String> name = Optional.ofNullable("tom");
String greeting = "Hello " + name.filter(s -> !s.isEmpty()).get();
System.out.println(greeting);
// 输出:Hello tom
Java 9 中 Optional 改进
Java 9 中 Optional 类有了一些改进,主要是增加了三个新的方法,分别是 stream()、ifPresentOrElse() 和 or()。这些方法可以让我们更方便地处理可能为空的值,以及和流或其他返回 Optional 的方法结合使用。我来详细讲解一下这些方法的作用和用法。
stream()
这个方法可以将一个 Optional 对象转换为一个 Stream 对象,如果 Optional 对象包含一个非空的值,那么返回的 Stream 对象就包含这个值,否则返回一个空的 Stream 对象。这样我们就可以利用 Stream 的各种操作来处理 Optional 的值,而不需要显式地判断是否为空。我们可以用 stream() 方法来过滤一个包含 Optional 的列表,只保留非空的值,如下所示:
List<Optional<String>> list = Arrays.asList(
Optional.empty(),
Optional.of("A"),
Optional.empty(),
Optional.of("B")
);
// 使用 stream() 方法过滤列表,只保留非空的值
List<String> filteredList = list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
System.out.println(filteredList);
// 输出 [A, B]
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
这个方法可以让我们在 Optional 对象包含值或者为空时,执行不同的操作。它接受两个参数,一个是 Consumer 类型的 action,一个是 Runnable 类型的 emptyAction。如果 Optional 对象包含一个非空的值,那么就执行 action.accept(value),如果 Optional 对象为空,那么就执行 emptyAction.run()。这样我们就可以避免使用 if-else 语句来判断 Optional 是否为空,而是使用函数式编程的方式来处理不同的情况。我们可以用 ifPresentOrElse() 方法来打印 Optional 的值,或者提示不可用,如下所示 :
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse(
x -> System.out.println("Value: " + x),
() -> System.out.println("Not Present.")
);
optional = Optional.empty();
optional.ifPresentOrElse(
x -> System.out.println("Value: " + x),
() -> System.out.println("Not Present.")
);
// 输出:Value: 1
// 输出:Not Present.
or(Supplier<? extends Optional<? extends T>> supplier)
这个方法可以让我们在 Optional 对象为空时,返回一个预设的值。它接受一个 Supplier 类型的 supplier,如果 Optional 对象包含一个非空的值,那么就返回这个 Optional 对象本身,如果 Optional 对象为空,那么就返回 supplier.get() 返回的 Optional 对象。这样我们就可以避免使用三元运算符或者其他方式来设置默认值,而是使用函数式编程的方式来提供备选值。我们可以用 or() 方法来设置 Optional 的默认值,如下所示:
Optional<String> optional = Optional.of("Hello ");
Supplier<Optional<String>> supplier = () -> Optional.of("tom");
optional = optional.or(supplier);
optional.ifPresent(x -> System.out.println(x));
optional = Optional.empty();
optional = optional.or(supplier);
optional.ifPresent(x -> System.out.println(x));
// 输出:Hello
// 输出:tom
为什么我推荐你使用 Optional 类
最后我总结一下使用 Optional 类的几个好处:
- 可以避免空指针异常,提高代码的健壮性和可读性。
- 可以减少显式的空值检查和 null 的使用,使代码更简洁和优雅。
- 可以利用函数式编程的特性,实现更灵活和高效的逻辑处理。
- 可以提高代码的可测试性,方便进行单元测试和集成测试。
总之,Optional 类是一个非常有用的类,它可以帮助我们更好地处理可能为空的值,提高代码的质量和效率。所以我强烈推荐你在 Java 开发中使用 Optional 类,你会发现它的魅力和好处。
关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!
null 不好,我真的推荐你使用 Optional的更多相关文章
- 不要再用if(xxx != null)或者try catch NullPointerException了,Optional可以帮你解决
public static void testIfPresent() { Map<String, Map<String, String>> map = new HashMap& ...
- Optional类与使用==判断null有什么区别?使用Optional类有什么优势?
1.使用object==null的例子 2.null带来的问题 3.其他语言中null的处理(替代) 4.Java8的Optional类 4.1 这样做有什么好处呢? 4.2 引入Optional类的 ...
- Guava API学习之Optional 判断对象是否为null
java.lang.NullPointerException,只要敢自称Java程序员,那对这个异常就再熟悉不过了.为了防止抛出这个异常,我们经常会写出这样的代码: Person person = p ...
- Java 中遇到null 和为空的情况,使用Optional来解决。
Java 中遇到null 和为空的情况,使用Optional来解决 示例代码: package crazy; import java.util.Optional; class Company { pr ...
- JDK8 Java 中遇到null 和为空的情况,使用Optional来解决。
空指针是我们最常见也最讨厌的异常,写过 Java 程序的同学,一般都遇到过 NullPointerException :) 初识null 详细可以参考[jdk 1.6 Java.lang.Null.P ...
- Java8:使用 Optional 处理 null
写过 Java 程序的同学,一般都遇到过 NullPointerException :) —— 为了不抛出这个异常,我们便会写如下的代码: User user = getUserById(id); i ...
- Google Guava之Optional优雅的使用null
为什么使用optional 使用Optional<T>除了简化粗鲁的if(null == object).降低函数的复杂度.增加可读性之外,它是一种傻瓜式的防护,Optional<T ...
- [转]Java 8 Optional类深度解析(null处理)
原文链接:http://www.importnew.com/6675.html 本文由 ImportNew - 高俊阳 翻译自 javacodegeeks.欢迎加入翻译小组.转载请见文末要求. 身为一 ...
- Java 8 (9) Optional取代null
NullPointerException,大家应该都见过.这是Tony Hoare在设计ALGOL W语言时提出的null引用的想法,他的设计初衷是想通过编译器的自动检测机制,确保所有使用引用的地方都 ...
- java8 用Optional取代null
如何处理null 怎样做才能避免不期而至的NullPointerException呢?通常,可以在需要的地方添加null的检查(过于激进的防御式检查甚至会在不太需要的地方添加检测代码),并且添加的方式 ...
随机推荐
- 2021-3-13 xml的增删改查
public void XmlAdd(string filename, List<People> pList) { try { List<People> peoples = X ...
- JVM 常见错误汇总
栈内存溢出 栈内存错误包括:栈帧过多(StackOverflowError).栈帧过大(OutOfMemoryError) StackOverflowError:如果线程请求的栈深度大于虚拟机所允许的 ...
- ROC 曲线与 PR 曲线
ROC 曲线与 PR 曲线 ROC 曲线与 PR 曲线 ROC 曲线和 PR 曲线是评估机器学习算法性能的两条重要曲线,两者概念比较容易混淆,但是两者的使用场景是不同的.本文主要讲述两种曲线的含义以及 ...
- 【Qt6】QWidgetAction 的使用
在开始主题前,先看一个 C++ 例子: #include <iostream> struct Data { int a; int b; }; // 注意这里 struct Data *s; ...
- 部署安装zookeeper集群
版本:3.7.0 节点IP: 172.50.13.103 172.50.13.104 172.50.13.105 获取安装包: wget http://mirrors.ustc.edu.cn/apac ...
- TensorRT 模型加密杂谈
在大多数项目交付场景中,经常需要对部署模型进行加密.模型加密一方面可以防止泄密,一方面可以便于模型跟踪管理,防止混淆. 由于博主使用的部署模型多为TensorRT格式,这里以TensorRT模型为例, ...
- Blazor前后端框架Known-V1.2.12
V1.2.12 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Git ...
- Programming abstractions in C阅读笔记p111-p113: boilerplate
<Programming Abstractions In C>学习第47天,p111-p113,总结如下: 一.技术总结 1.boilerplate /* * File: random.h ...
- SpringBoot3集成RocketMq
标签:RocketMq5.Dashboard: 一.简介 RocketMQ因其架构简单.业务功能丰富.具备极强可扩展性等特点被广泛应用,比如金融业务.互联网.大数据.物联网等领域的业务场景: 二.环境 ...
- 微服务集成seata完成分布式事务,解决数据不一致问题
细心的盆友可能已经发现了,我们的跨行转账并没有保证数据一致性,比如小明扣除了100,但是因为各种问题小红在添加100金额的时候遇到了异常,这个时候数据就出现不一致性 我们可以选择seata来进行分布式 ...