前篇文章已经总结了集合的操作是如何在Java8优化的:函数编程和Stream_txxs的博客-CSDN博客,这篇文章总结一下对于对象如何利用Java8进行操作,这样对于大部分代码都可以用Java8的语法进行操作了。
一、 Optional 是什么
Optional 的作者 Brian Goetz 对这个 API 的说明:
Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.
为了避免 null 带来的错误,我们提供了一个可以明确表示空值的有限的机制。
Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。
二、Optional API介绍
public static<T> Optional<T> empty(); // 返回一个Optional容器对象
public static <T> Optional<T> of(T value); // 创建一个Optional对象,如果 value 是 null,则抛出 NPE
public static <T> Optional<T> ofNullable(T value); // 创建一个Optional对象,但 value 为空时返回Optional.empty()
public T get(); // 返回Optional中包装的值,在判空之前,千万不要直接使用
public boolean isPresent(); // 判断Optional中是否有值,返回 boolean
public void ifPresent(Consumer<? super T> consumer); // 判断Optional中是否有值,有值则执行 consumer,否则什么都不干
public T orElse(T other); // 返回Optional中包装的值,但不同的是当取不到值时,返回你指定的 default
public T orElseGet(Supplier<? extends T> other); // 返回Optional中包装的值,取不到值时,返回你指定的 default
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier); // 返回Optional中包装的值,取不到值时抛出指定的异常
三、最佳实践
3.1、Optional(T value),empty(),of(T value),ofNullable(T value)
这四个函数之间具有相关性,因此放在一组进行记忆。
先说明一下,Optional(T value)
,即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)
构造函数的源码,如下图所示
那么,of(T value)的源码如下
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的对象,大概就是长下面这样的
public final class Optional<T> {
//省略....
private static final Optional<?> EMPTY = new Optional<>();
private Optional() {
this.value = null;
}
//省略...
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
}
那么,empty()
的作用就是返回EMPTY对象。
好了铺垫了这么多,可以说ofNullable(T value)
的作用了,上源码
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
对象。
那是不是意味着,我们在项目中只用ofNullable
函数而不用of函数呢?
不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NullPointerException
。而是要立即报告,这种情况下就用Of函数。但是不得不承认,这样的场景真的很少。博主也仅在写junit测试用例中用到过此函数。
前置对象,以下所有的例子都适用这个对象进行操作
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class User {
// 兴趣id列表
private List<Long> favList;
// 标签列表
private List<Tag> tagList;
private String name ="测试长度";
private Optional<String> nameOp = Optional.ofNullable("测试op");
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Tag {
private Long id;
}
@Override
public String toString() {
return "User{" +
"favList=" + favList +
", tagList=" + tagList +
", name='" + name + '\'' +
'}';
}
/**
* 获取兴趣id列表
*/
public List<Long> getFavList(User user) {
return Optional.ofNullable(user)
.map(User::getFavList)
.map(Collection::stream)
.orElse(Stream.empty())
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 获取标签id列表
*/
public List<Long> getTagIdList(User user) {
return Optional.ofNullable(user)
.map(User::getTagList)
.map(Collection::stream)
.orElse(Stream.empty())
.map(Tag::getId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
构造使用例子
/**
* 构造相关内容empty、of和ofNullable
*
*/
public static void create(){
User user = null;
System.out.println(Optional.empty());//输出对象
System.out.println(Optional.of(user));//会直接抛出异常,因为of不能允许为空
Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);
System.out.println(user1);//Optional.empty,不会抛出异常
User user2 = new User();
Optional<User> user3 = Optional.ofNullable(user2).filter(u -> u.getName().length()<6);
System.out.println(user3);//Optional[User{favList=null, tagList=null, name='测试长度'}],name需要赋值,不然u.getName()会抛出异常
}
3.2、orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)
这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于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("zhangsan");
return user;
}
这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法,大家可自行测试。
至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示
User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));
例子
/**
* 构造函数传入的value值为null时,进行调用的
* orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)
* 这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法
*/
public static void ifnull(){
User user = null;//User user = new User();orElseGet就不执行createUser方法了
User user1 = Optional.ofNullable(user).orElse(createUser());
System.out.println(user1);//User{favList=null, tagList=null, name='测试长度'}
User user2 = Optional.ofNullable(user).orElseGet(() -> createUser());
System.out.println(user2);//User{favList=null, tagList=null, name='测试长度'}
try {
//会抛出异常,所以需要捕获异常
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));
} catch (Exception exception) {
exception.printStackTrace();
}
}
public static User createUser(){
System.out.println("方法被执行");
return new User();
}
3.3、map(Function<? super T, ? extends U> mapper)和flatMap(Function<? super T, Optional> mapper)
这两个函数放在一组记忆,这两个函数做的是转换值的操作。直接上源码
public final class Optional<T> {
//省略....
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));
}
}
//省略...
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 city = Optional.ofNullable(user).map(u-> u.getName()).get();
对于flatMap而言:
如果User结构是下面这样的
public class User {
private String name;
public Optional<String> getName() {
return Optional.ofNullable(name);
}
}
这时候取name的写法如下所示
String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();
例子
/**
* map和flatMap,两者的区别是前者是正常的对象,后者是Optional对象
*/
public static void opmap(){
User user = new User();
String name = Optional.ofNullable(user).map(u-> u.getName()).get();
System.out.println(name);//测试长度
String nameOp = Optional.ofNullable(user).flatMap(u-> u.getNameOp()).get();
System.out.println(nameOp);//测试op
//及联map
Optional.ofNullable(user)
.map(o -> o.getTagList())
.map(d -> d.get(0))
.filter(d -> d.getId() > 1)
.orElse(new User.Tag());
}
3.4、isPresent()和ifPresent(Consumer<? super T> consumer)
这两个函数放在一起记忆,isPresent
即判断value值是否为空,而ifPresent
就是在value值不为空时,做一些操作。这两个函数的源码如下
public final class Optional<T> {
//省略....
public boolean isPresent() {
return value != null;
}
//省略...
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
}
需要额外说明的是,大家千万不要把
if (user != null){
// TODO: do something
}
给写成
User user = Optional.ofNullable(user);
if (Optional.isPresent()){
// TODO: do something
}
因为这样写,代码结构依然丑陋。博主会在后面给出正确写法
至于ifPresent(Consumer<? super T> consumer)
,用法也很简单,如下所示
Optional.ofNullable(user).ifPresent(u->{
// TODO: do something
});
例子:
/**
* isPresent()和ifPresent(Consumer<? super T> consumer)
*/
public static void ifpre(){
User user = new User();
if (user != null){
// do something
}
//上边这种形式千万不要写成下边这种形式,这样写,代码结构依然丑陋
Optional<User> user1 = Optional.ofNullable(user);
if (user1.isPresent()){
// do something
}
//正确写法场景1
user1.orElseGet(() -> new User());
//而不是
if(user1.isPresent()){
user1 = user1;
}else {
user1 = Optional.ofNullable(new User());
}
//正确写法2
user1.ifPresent(System.out::println);
//而不是
if (user1.isPresent()) {
System.out.println(user1.get());
}
//正确写法3
user1.map(u -> u.getFavList()).orElse(Collections.emptyList());
//而不是
if(user1.isPresent()) {
List<Long> result = user1.get().getFavList();
} else {
List<Long> result = Collections.emptyList();
}
//正确写法4,由于map可以无限及联
user1.map(u -> u.getName())
.map(name -> name.toUpperCase())
.orElse(null);
//而不是
User user3 = new User();
String name = null;
if(user3 != null) {
name = user.getName();
if(name != null) {
name.toUpperCase();
} else {
user = null;
}
} else {
user = null;
}
//正确写法5
Optional.ofNullable(user)
.ifPresent(u->{
//dosomething(u);
});
//而不是
if(user!=null){
//dosomething(user);
}
//正确写法6
Optional.ofNullable(user).map(u -> u.getName()).orElse("unknown");
//而不是
Optional<User> u = Optional.ofNullable(user);
if(!u.isPresent()){
//dosomething
}else{
//dosomething
}
}
3.5、filter(Predicate<? super T> predicate)
不多说,直接上源码
public final class Optional<T> {
//省略....
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
filter 方法接受一个 Predicate
来对 Optional
中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty
。
用法如下
Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);
如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。
例子:
public static void useFilter(){
User user2 = new User();
Optional<User> user3 = Optional.ofNullable(user2).filter(u -> u.getName().length()<6);
System.out.println(user3);//Optional[User{favList=null, tagList=null, name='测试长度'}],name需要赋值,不然u.getName()会抛出异常
}
四、简单总结:
1. 不要用get()和orElse(),而用orElseGet();
2. 不要直接返回null,使用 Optional.empty()
3. 不要用of(),而用ofNullable()
4. 不要使用isPresent(),get(),这是倒退(轻一点叫不推荐)
5. 大胆使用map、filter 重构代码
6. Optional不建议用于函数的入参
7. Optional不能被序列化,因此不能使用于字段
五、参考
https://blog.csdn.net/jack1liu/article/details/124064925
https://caoyunhao.com/p/c794fc/
https://xie.infoq.cn/article/e3d1f0f4f095397c44812a5be
https://www.aqwdzy.com/content/84
https://zhuanlan.zhihu.com/p/128481434
https://www.jianshu.com/p/362010f310b9
https://juejin.cn/post/7174292017415979038
https://juejin.cn/post/6962035387787116551