前言
开发相关知识点整理,内容来自我的个人网站笔记,和收集参考的资料,由于是发在社区,整理了排版和可读性,对于内容我尽量做到是经过验证的,以免误人子弟。当然本人能力有限,如有错误或疑问,欢迎一起讨论和指正(有些内容也是我参考别人的,如有侵权,请联系我,一般我会注明转载和参考原文)
1、什么是Optional?
一句话概括: 它是一个容器,用来包装对象,解决NullPointerException异常的
2、如何创建Optional对象?或者说如何用Optional来包装对象或null值?
-
static Optional empty() :用来创建一个空的Optional
-
static Optional of(T value) :用来创建一个非空的Optional
-
static Optional ofNullable(T value) :用来创建一个可能是空,也可能非空的Optional
其实上面这三个方法,看一下源码就很清晰了,比如of
// 方法
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
// 构造器
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
// Objects对象的requireNonNull方法
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
of方法创建Optional对象,并对传入值做NullPointerException异常判断,所以,当你传递一个null值的时候,就会触发异常了
再看看empty方法
// 方法
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
// 静态常量
private static final Optional<?> EMPTY = new Optional<>();
// 常量
private final T value;
// 构造器
private Optional() {
this.value = null;
}
一开始就定义了一个Optional<?> EMPTY
的对象,并且构造函数使用默认的,value为空。empty只是做了泛型的转换
剩下的ofNullable就更简单了,通过传递的值决定使用of还是empty
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
Optional类的源码还是比较简单的,代码很少,通过成员变量和构造方法的解读,相信你已经理解了of,empty,ofNullable
3、如何使用Optional类?
最正确的做法就是对需要做NullPointerException异常判断的对象,把它包装成Optional类,然后指明对象不存在的时候,应该怎么做,下面来看一下几种常见的用法
1. orElse
public class OptionalTest {
public static void main(String[] args) {
HashMap<Integer, User> userHashMap = new HashMap<>();
userHashMap.put(1, new User(1, "Xiao Ming"));
userHashMap.put(2, new User(2, "Xiao Zhi"));
userHashMap.put(3, null);
UserUtil userUtil = new UserUtil(userHashMap); // 这个工具类只是为了填充数据的,不要在意这些细节,getUserByUserId方法能返回User对象
// 包装了User对象,并且使用orElse指明了对象不存在的时候应该返回指定的值,也就是new User(1, "Xiao Bai")
User user = Optional
.ofNullable(UserUtil.getUserByUserId(2))
.orElse(new User(1, "Xiao Bai"));
}
}
// 工具类,随便写的,通过hashMap模拟查询用户
public class User {
public Integer id;
public String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
}
class UserUtil {
public static HashMap<Integer, User> hashMap;
UserUtil(HashMap<Integer, User> hashMap) {
UserUtil.hashMap = hashMap;
}
public static User getUserByUserId(Integer id) {
User user = hashMap.get(id);
System.out.println(user);
return user;
}
}
2. orElseGet
和 orElse 不同,它的参数接受一个lambda表达式
User user = Optional
.ofNullable(UserUtil.getUserByUserId(2))
.orElseGet(() -> new User(1, "Xiao Bai"));
3. orElseThrow
同理,传递一个lambda表达式异常(注意啊,方法是限定了参数的,触发异常就用orElseThrow,不能用orElseGet)
User user = Optional
.ofNullable(UserUtil.getUserByUserId(2))
.orElseThrow(()-> new AssertionError("AssertionError"));
4. map
调用map后,如果当前 Optional 为 Optional.empty,则依旧返回 Optional.empty;否则返回一个新的 Optional,该 Optional 包含的是:函数 mapper 在以 value 作为输入时的输出值
比如下面的例子,第一次调用map后,获取的是name,传递给下一个map的值相当于Optional.ofNullable(name)
String user = Optional.ofNullable(UserUtil.getUserByUserId(1))
.map(user1 -> user1.name)
.map(String::toLowerCase)
.orElse("123");
System.out.println(user);
// 方法
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));
}
}
这个操作可以用在对对象做多次操作的场景下,并且保证为空的时候返回指定的值,比如先获取用户的名称,再获取用户的电话,做一下判断后,再通过电话查询到其它的数据,然后继续...,最后如果某一环节出现异常,那就返回orElse定义的对象
5. flatMap
flatMap 方法与 map 方法的区别在于,map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional
即 .flatMap(user -> Optional.of(user.name()))
6. filter
都是差不多的套路,这次我们先看源码,可以发现同样是接受lambda表达式,并且要是一个Boolean返回值的,如果本次操作的optional是empty的,就返回本身
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
// 函数式接口部分代码,可以看到test是要求返回Boolean类型的
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
// 例子
String name = Optional.ofNullable(UserUtil.getUserByUserId(1))
.filter(user -> user.id == 2)
.map(user -> user.name)
.orElse("abc");
System.out.println(name);
通过观察源码,我们可以看到,很多为空的都会返回empty,这让各个操作都能够互相调用
总结
Optional是一个比较简单的类,推荐直接阅读源码,通过简单的包装,很优雅的解决的空指针问题,以前谷歌Guava库就实现了,后面Java8正式把规范加到 java.util.Optional
类中。
另外,上面的做法是对于程序内的,如果是web开发,参数校验,请使用Hibernate-Validator即可,作为一个合格的后端,我不会让前端挑刺的😀