前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为啥 Java 中不推荐将 Optional 当做参数使用?

为啥 Java 中不推荐将 Optional 当做参数使用?

作者头像
明明如月学长
发布2021-12-27 13:12:45
2.9K0
发布2021-12-27 13:12:45
举报
文章被收录于专栏:明明如月的技术专栏

一、背景

最近开发过程中,身边的同事为了实现逻辑复用,定义一个私有公共方法实现逻辑复用,定义函数签名时将上游的 Optional 作为参数传递。 IDEA 给出警告,但是并没有讲清楚为什么。

效果如下:

在这里插入图片描述
在这里插入图片描述

Optional 怎么使用不是本文的重点,如果想掌握可以参考 《Java 8 实战》^「1」 自行学习。

本文主要聊为什么不让作为参数使用。

工作过几年的人能够发现一个规律,线上出现的异常很大比例都是空指针。

Java 8 引入 Optional 主要是为了避免出现空指针;避免代码中出现各种 null 检查等。

那么,为什么不推荐作为参数使用呢?

二、讨论

2.1 为什么不要将 Optional 作为参数

如果将 Optional 当做参数使用,那么本身可传递 null, 依然需要进行判空再使用。 并不能有效避免空指针,甚至带来额外的判断。

案例1: 直接使用 String :

代码语言:javascript
复制
public String doSomething(String name) {
  if (name == null) {
    return "你好";
  } else {
    return "你好 " + name;
  }
}

使用 Optional 作参数:

代码语言:javascript
复制
public String doSomething(Optional<String> name) { 
  if (name == null || !name.isPresent()) {
    return "你好";
  } else {
    return "你好" + name;
  }
}

示例2: 由于我们通常都是将 Optional 当做返回值使用,潜意识认为不会传递 null, 通常就直接使用:

代码语言:javascript
复制
public static List<Person> search(List<Person> people, String name, Optional<Integer> age) {
    if(CollectionUtils.isEmpty(people)|| null == name ){
    return new ArrayList<>();
    }
    
    return people.stream()
            .filter(p -> p.getName().equals(name))
            .filter(p -> p.getAge().get() >= age.orElse(0))
            .collect(Collectors.toList());
}

如果代码比较复杂,其他程序员不容易注意到这点,他可能会认为不需要校验 age ,因此就传 null:

someObject.search(people, "Peter", null);

结果造成了空指针!!

代码语言:javascript
复制
public static List<Person> search(List<Person> people, String name, Integer age) {
    if(CollectionUtils.isEmpty(people)|| null == name ){
       return new ArrayList<>();
    }
    
    final Integer ageFilter = age != null ? age : 0;

    return people.stream()
            .filter(p -> p.getName().equals(name))
            .filter(p -> p.getAge().get() >= ageFilter)
            .collect(Collectors.toList());
}

因此,尽量避免将 Optional 作为参数使用。

本质上是 Optional 作参数时,上游通常可以自己构建 Optional 或者取下游某个调用的返回值传递。

当使用某个调用返回值传递时,通常不会出现空指针,但是自己去执行调用传递 null 时很容易出现空指针。

2.2 非要当做参数怎么办?

有些场景希望直接将下游的返回值作为参数传递。

模拟示例如下:

代码语言:javascript
复制
    private static String first(String someParam){
     return   something( "first",  someParam, invokeSomeFunction(someParam));
    }


    private static String second(String someParam){
        return   something( "second",  someParam, invokeOtherFunction(someParam));
    }


    private  static <T> T something(String name ,String someParam,Optional<T> optional){

        // 各种公共逻辑

        return optional.get();
    }

    // 模拟下游接口1
    private static Optional<String> invokeSomeFunction(String someParam){
        return Optional.of(someParam);
    }

    // 模拟下游接口2
    private static Optional<String> invokeOtherFunction(String someParam){
        return Optional.of(someParam);
    }

下游返回 Optional是合理的,但我们又不能将 Optional 作为参数传递。

因此有如下写法:

代码语言:javascript
复制
    private static String first(String someParam){
     return   something( "first",  someParam, invokeSomeFunction(someParam).orElse(null));
    }


    private static String second(String someParam){
        return   something( "second",  someParam, invokeOtherFunction(someParam).orElse(null));
    }


    private  static <T> T something(String name ,String someParam,T param){

        // 各种公共逻辑

        return null;
    }

如果自定义方法过多,都要 orElse 去转为非 Optional 对象,显然不太优雅。

其实,这种场景本质上是希望将调用作为参数传递下去,因此想到了直接使用 Supplier 或者 Function 等。

代码语言:javascript
复制
   private static String first(String someParam){
     return   something( "first",  someParam, ()->invokeSomeFunction(someParam));
    }


    private static String second(String someParam){
        return   something( "second",  someParam, ()->invokeOtherFunction(someParam));
    }


    private  static <T> T something(String name , String someParam, Supplier<Optional<T>> optional){

        // 各种公共逻辑

        return  null;
    }

这样 Optional 依然是作为返回值使用,参数是方法调用 Supplier 也不违规,又契合将调用传递的目的。

2.3 Optional 不是万能的

Optional虽然能够减少空指针,但是滥用也会降低代码可读性。

Optional本身没有实现序列化接口,做属性时,如果使用 JDK 序列化将会报错。 可以使用 guava 包里的 Optional类替代。

三、结论

【建议】不建议将 Optional 作为参数,容易造成空指针和误解,这和 Optional 的目的相违背。如果是想传递某个调用,请使用 Supplier。 【建议】不建议将 Optional 作为属性,非要用建议使用 guava 包的 Optional 类。

参考文献

[1] 厄马(Raoul-Gabriel Urma) / 弗斯科(Mario Fusco) / 米克罗夫特(Alan Mycroft)《Java 8 实战》.人民邮电出版社 [2] https://rules.sonarsource.com/java/RSPEC-3553 [3] https://www.baeldung.com/java-optional

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/12/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、讨论
    • 2.1 为什么不要将 Optional 作为参数
      • 2.2 非要当做参数怎么办?
        • 2.3 Optional 不是万能的
        • 三、结论
        • 参考文献
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档