V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
javaWeber
V2EX  ›  Java

请教下 java8 的 Optional。。

  •  
  •   javaWeber · 2020-03-25 00:12:19 +08:00 · 4638 次点击
    这是一个创建于 1698 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近写了一堆空指针,打算改用下 java8 的 Optional 。。

    请问下以下代码,怎么用 Optional 表示。

    1. 判断 A 是否为空,然后修改 B 或者执行其他逻辑。

    想过用 if (isPresent())去判空,又觉得跟之前的 if 没什么区别。。

    String str="123";
    String result="abc";
    if(str!=null){
    	result+="def";
    }
    

    2.判断 A 是否为空,然后根据情况执行 if else 里面的逻辑。。

    java9 有个 ifPresentOrElse(),不过项目里用的是 java8 。

    String str="123";
    String result="abc";
    if(str!=null){
    	result+="def";
    }else {
        result+="ghi";
    }
    
    第 1 条附言  ·  2020-03-25 13:56:48 +08:00
    我这里的例子不太准确,判断的是字符串,应该改成对象的。

    我想避免对象判空,也就是用 Optional 减少 if(对象!=空)这类操作。。
    31 条回复    2020-08-13 10:06:24 +08:00
    javaWeber
        1
    javaWeber  
    OP
       2020-03-25 00:29:16 +08:00
    3.还有就是 字符串类型的 Optional.orElse(),这里面 orElse 的默认值用什么好?

    我看网上很多用.orElse("default"),为空时就给了个默认值"default"或者"unknown"。。感觉很奇怪。
    alphatoad
        2
    alphatoad  
       2020-03-25 00:32:33 +08:00
    用 FP 的思路就很好理解了,相当于 Haskell 的 Maybe
    data Maybe a = Nothing | Just a
    weakish
        3
    weakish  
       2020-03-25 00:33:10 +08:00
    A 是否为空决定是否对 B 进行操作,这里对 B 的操作并没有用到 A 的值,不会碰到空指针,也就没必要用 Optional 。
    weakish
        4
    weakish  
       2020-03-25 00:37:50 +08:00
    "default" 或者 "unknown" 这是教程里为了演示用的吧。实际场景中,就是应该用什么默认值就用什么默认值呀,原来用 `a == null ? a : "xxx"`,现在改成 `a.orElse("xxx")`,只是写法变一下,并不影响默认值的选取。
    bkmi
        5
    bkmi  
       2020-03-25 00:53:26 +08:00 via Android
    要不用 kotlin 吧
    但是你这两个例子,看不出哪 NPE,就你现在的写法最简单了…
    lxychn
        6
    lxychn  
       2020-03-25 04:32:29 +08:00
    不用 optional 的话判断为空的 if 忘了写就可能会出错,用了 optional 的一定要用 ifPresent()去判断,不然 compiler 就会报错,避免忘了检查是否为空的情况

    optional 好像和 lamda 结合用的更多
    xixinimei
        7
    xixinimei  
       2020-03-25 05:09:01 +08:00
    当你意识到一个变量该用 optional 的时候,可以不用 optional 。

    Java 的 Optional 表达能力有限,主要是对自由变量的重新赋值很麻烦,需要用到容器类来包装。
    optional
        8
    optional  
       2020-03-25 08:01:27 +08:00 via iPhone   ❤️ 3
    Optional 的精髓在 map ifpresent stream
    noreplay
        9
    noreplay  
       2020-03-25 08:05:47 +08:00 via Android   ❤️ 2
    @optional 结合 id 来看,特别让人信服🤓🤓
    chendy
        10
    chendy  
       2020-03-25 08:23:11 +08:00
    Optional 的有点是可以挂回调,配合其他(比如 stream ) api 可以一路点点点点写完一套逻辑,但是如果不习惯挂回调的话就很别扭
    简单场景单独判断 null 足以
    yty2012g
        11
    yty2012g  
       2020-03-25 08:45:02 +08:00
    第一,如你图上的示例,确实使用 Optional 和判断是否等于 null 没有区别,因为你的条件分支里面没有使用 str 这个变量。在此情况下,我认为没有区别。
    第二,假如你的条件分支有使用 str,那就需要对 str 为 null 的情况赋予其他值,此时可以使用 Optional.orElse("default")或者 Optional.orElseGet(()->"default")这样的方式来代替使用 str 。
    第三,还有一种情况是 str 等于 null 我要抛异常,很常见,例如 DB 里面查不到数据我要抛异常,此时可以使用 Optional.orElseThrow(() -> new StrNotFoundException("str 没找到!"))类似的.
    综上,不是什么情况下 Optional 都能达到你想要的效果,不过最差的基本面也是和判断是否为 null 类似,所以可以尝试去使用
    guyeu
        12
    guyeu  
       2020-03-25 10:09:01 +08:00
    @javaWeber #1 如果不知道用什么好,就用 orThrow,抛出异常。
    CommandZi
        13
    CommandZi  
       2020-03-25 10:19:42 +08:00
    你这两个例子,不管 str 是不是 null,都不会空指针吧。
    Aresxue
        14
    Aresxue  
       2020-03-25 10:35:29 +08:00
    optional.ifPresent(修改 B), 但就像上面说的你这个简单调用还不涉及 npe 没必要使用
    cyspy
        15
    cyspy  
       2020-03-25 10:52:24 +08:00
    flatmap
    mezi04
        16
    mezi04  
       2020-03-25 11:26:19 +08:00
    个人理解:把 optional 想想成盒子,of 或者 ofNullable 传入的参数就是盒子里边的东西。optional 的目的是从这个盒子里拿出一个非空数据,你可以通过一系列的方法转换盒子里的数据( fliter/flatMap ),或者告诉盒子,如果为空怎么做( orElse/ifPresent 等)。如果你的模型不符合这个,就没有用 optional 的必要
    hantsy
        17
    hantsy  
       2020-03-25 13:42:43 +08:00
    用 orElseThrow 抛出异常就好了,我也比较常用。
    Optional<Post> post = posts.findById(id)

    post.map(....).orElseThrow(()->new PostNotFoundException(id))
    hantsy
        19
    hantsy  
       2020-03-25 13:48:15 +08:00   ❤️ 1
    Optional 本身就是解决这中 If Null 判断,如果又回到 If 来判断,还不如不用。
    javaWeber
        21
    javaWeber  
    OP
       2020-03-25 13:59:16 +08:00
    @CommandZi 你这两个例子,不管 str 是不是 null,都不会空指针吧。
    ==============================================
    我这里的例子写得不太准确,判断的是字符串,应该改成判断对象是否为空的。
    ```
    if(对象!=空){

    }
    ```
    MakHoCheung
        22
    MakHoCheung  
       2020-03-25 14:07:25 +08:00
    Optional 针对的是 str 。
    这代码就算放到 kotlin 也要判 null,因为你的 str 是 String?类型
    XiLemon
        23
    XiLemon  
       2020-03-25 16:50:55 +08:00
    可以参考一下: http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy 关于 NULL 的处理建议
    ica10888
        24
    ica10888  
       2020-03-25 17:39:44 +08:00
    其实这个是函数式编程的东西,相当于一个包装。不过一般情况下用来处理链式操作,如 getA().getB().getC() 的空指针异常,其实 kotlin 的语法糖要好看一些...
    tachikomachann
        25
    tachikomachann  
       2020-03-25 17:54:23 +08:00
    我的理解是用 Optional 没办法减少判空操作,而是强制你必须判空。所以一旦用了 Optional,就不会无意识地写出 NPE 问题的代码。。
    lyyhello
        26
    lyyhello  
       2020-03-25 18:57:11 +08:00
    你看下 jdk8 用到 Optional 的地方的源码就知道了。如果有明确的返回,不用 Optional,如果因为各种原因(),结果不能保证一定为空,就用 Optional 返回。而且使用者不是简单的判断是否为空,而是和 Stream 紧密联系着用呢
    Jrue0011
        27
    Jrue0011  
       2020-03-26 10:46:30 +08:00
    Optional 要用上 map/flatMap 、filter 。一般应该是一段代码调用多个方法,每个方法入参都是上个方法返回值或返回值部分,Optional 的 map/flatMap 可以避免对每个返回值判断 null,如果对返回值的判断不仅仅是否 null 的话,可以用 filter 判断。

    比如这样。。。

    Optional.of(userId).map(userDao::getPhone).filter(phoneService::isChinaMobile).ifPresent(smsService::sendMsg);
    Optional.of(userId).map(userDao::getPhone).map(phoneBook::getContactListByPhone).orElseGet(Collections::emptyList);
    或者如果有需要抛业务异常,也可以用 orElseThrow
    CommandZi
        28
    CommandZi  
       2020-03-26 11:15:28 +08:00
    @javaWeber Optional 设计是用来减少空指针异常,不是减少 if(对象!=空)这类操作的。你 if 后续代码都没调用对象,说明这只是你程序的固定逻辑而已,不管对象用不用 optional,都是要判断的。
    CommandZi
        29
    CommandZi  
       2020-03-26 11:17:07 +08:00
    @CommandZi 当然这是我使用 Swift 当中的 Optional 的经验,Java 的没有了解。
    MotherShip
        30
    MotherShip  
       2020-04-01 21:43:55 +08:00
    手头上用的比较优雅的玩法大概是,Repository 层返回一个 Optional 对象,为空则直接一路把异常抛到 Controller 层然后统一处理:

    ```java
    String a = Optional.ofNullable("a").orElseThrow(()->{throw new RuntimeException();});
    ```
    至于你这两个例子。。确实用不用没啥区别
    facelezz
        31
    facelezz  
       2020-08-13 10:06:24 +08:00
    楼上回答了那么多,其实 guava 文档里就有解释。
    Besides the increase in readability that comes from giving null a name (增加了可读性,isPresent()比 if xxx!=null 可读性好)
    the biggest advantage of Optional is its idiot-proof-ness.It forces you to actively think about the absent case if you want your program to compile at all, since you have to actively unwrap the Optional and address that case (使用了 option 后,强迫你思考不存在的情况,避免忘记)

    This is especially relevant when you're returning values that may or may not be "present." You (and others) are far more likely to forget that other.method(a, b) could return a null value than you're likely to forget that a could be null when you're implementing other.method. Returning Optional makes it impossible for callers to forget that case, since they have to unwrap the object themselves for their code to compile.

    (对设计的优化,如果你的方法返回值可能存在也可能不存在,就尽量返回 optional,这样别人用你的方法就知道需要判断)
    至于 orElse 这种类型操作,我自己理解的是 4 楼那样,此外,optional 并不是用来简化流程,只是一种设计,让人记住去处理 null
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1464 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:29 · PVG 01:29 · LAX 09:29 · JFK 12:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.