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

Java8 的函数式编程支持,我学习后觉得很沮丧和鸡肋

  •  
  •   zazalu · 2019-10-10 19:16:07 +08:00 · 11080 次点击
    这是一个创建于 1869 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不知道是我问题还是什么,就感觉怪怪的

    72 条回复    2019-10-12 15:56:47 +08:00
    xlui
        1
    xlui  
       2019-10-10 19:33:38 +08:00
    您好,是你的问题,看一看没有函数式支持之前的代码就知道这东西解决了什么了。
    oneisall8955
        2
    oneisall8955  
       2019-10-10 20:11:18 +08:00 via Android   ❤️ 1
    function 包用起来还是美滋滋的,将方法封装成对象,支持 lambda 表达式,用习惯了就改不回来了
    qping
        3
    qping  
       2019-10-10 20:20:46 +08:00   ❤️ 2
    给你举个应用场景:
    有个 list 想提取一部分字段转成一个 List<String>
    ```
    List<TableInfo> tables = ...;
    List<String> tableNames = ...;
    for(TableInfo table : tables){
    String tableName = table.getName().toUpperCase();
    tableNames.add(tableName);
    }
    ```

    使用 lamda,只要一行:
    ```
    List<String> tableNames = tables.stream().map(v -> v.getTableName().toUpperCase()).collect(Collectors.toList());
    ```

    香不香
    fuyufjh
        4
    fuyufjh  
       2019-10-10 20:20:47 +08:00
    要么___,要么___,scala 欢迎你
    fuyufjh
        5
    fuyufjh  
       2019-10-10 20:21:42 +08:00
    @qping 这明明是使用了 stream api
    godlovesxcjtest
        6
    godlovesxcjtest  
       2019-10-10 20:23:24 +08:00
    @fuyufjh 但是这也是函数式编程啊
    fuyufjh
        7
    fuyufjh  
       2019-10-10 20:27:45 +08:00
    @godlovesxcjtest 您对 FP 可能有误解
    zhuangzhuang1988
        8
    zhuangzhuang1988  
       2019-10-10 20:43:27 +08:00   ❤️ 3
    @qping 不香, 代码没减多少, 而且更难看懂了,调试也不好,性能还不咋的.
    hbsfxlz
        9
    hbsfxlz  
       2019-10-10 23:02:13 +08:00
    @zhuangzhuang1988 难看懂的说明没有认真去了解过,stream 的可读性比 for 循环可读性强了一百倍
    chendy
        10
    chendy  
       2019-10-10 23:20:04 +08:00
    最常用的场景可能就是 StreamAPI 了
    还好 idea 已经有 stream debugger 了,否则 debug 真的痛苦…
    godlovesxcjtest
        11
    godlovesxcjtest  
       2019-10-10 23:57:23 +08:00 via Android
    @fuyufjh 求指教。
    taotaodaddy
        12
    taotaodaddy  
       2019-10-11 00:01:52 +08:00 via Android
    java 的 fp 实现堪称是见过的颇不优雅
    FireFoxAhri
        13
    FireFoxAhri  
       2019-10-11 00:35:56 +08:00 via Android
    你需要 scala
    Senventise
        14
    Senventise  
       2019-10-11 05:58:58 +08:00 via Android
    @taotaodaddy 你这句子有语病啊
    coldear
        15
    coldear  
       2019-10-11 06:26:32 +08:00
    kotlin 吧
    zazalu
        16
    zazalu  
    OP
       2019-10-11 08:30:37 +08:00
    @qping 这些简单的还能接受,确实简化了,主要是受不了那个 andThen()函数里产生函数的写法,觉得不是很直观
    zazalu
        17
    zazalu  
    OP
       2019-10-11 08:32:09 +08:00
    ```java
    import java.util.function.*;

    class I {
    @Override
    public String toString() { return "I"; }
    }

    class O {
    @Override
    public String toString() { return "O"; }
    }

    public class TransformFunction {
    static Function<I,O> transform(Function<I,O> in) {
    return in.andThen(o -> {
    System.out.println(o);
    return o;
    });
    }
    public static void main(String[] args) {
    Function<I,O> f2 = transform(i -> {
    System.out.println(i);
    return new O();
    });
    O o = f2.apply(new I());
    }
    }
    ```

    这个既有趣又感觉我玩的是 java 吗?
    araaaa
        18
    araaaa  
       2019-10-11 08:52:46 +08:00
    去用异步框架 vert.x 之类的
    Jrue0011
        19
    Jrue0011  
       2019-10-11 09:08:57 +08:00
    @zazalu lambda 挺好用的,如果你觉得一段方法链用了很多个 lambda 太长的话,可以定义把这些方法抽出来定义成静态方法或者成员方法,通过方法引用传递
    qping
        20
    qping  
       2019-10-11 09:13:35 +08:00
    @zhuangzhuang1988 #8 兄弟,你可以尝试用一下,一点也不复杂,当你了解后可读性并不差。最初我也是很抵制的,stream api 可以代替很多恶心的 for 循环的场景,用的飞起


    @zazalu #16 从代码的可读性来说还是 Function 接口是有点复杂,这些接口更像是给底层架构设计使用的。有的东西只需要了解,然后能读懂其他人的代码就可以了,日常你可以不用嘛。
    soeur
        21
    soeur  
       2019-10-11 09:17:44 +08:00 via Android
    我觉得挺好用的呀
    Jrue0011
        22
    Jrue0011  
       2019-10-11 09:18:00 +08:00
    大概是这样,手写没有编译器警告可能会有错误。。但是是能实现的

    import java.util.function.*;

    class I {
    @Override
    public String toString() { return "I"; }

    public O println() {
    System.out.println(this);
    return new O();
    }
    }

    class O {
    @Override
    public String toString() { return "O"; }

    public O println() {
    System.out.println(this);
    return this;
    }
    }

    public class TransformFunction {
    static Function<I,O> transform(Function<I,O> in) {
    return in.andThen(O::println);
    }

    public static void main(String[] args) {
    Function<I,O> f2 = transform(I::println);
    O o = f2.apply(new I());
    }
    }
    LeeSeoung
        23
    LeeSeoung  
       2019-10-11 09:22:35 +08:00
    技术每天都在更新,不是你能挑三拣四的时候,今天还在为掌握了这些而满足的时候,明天就有一个更高效的技术就替代它了,一生都需要学习。
    zzxop
        24
    zzxop  
       2019-10-11 09:31:57 +08:00
    建议你再看一下,了解了之后肯定不想再用以前的方法
    guolaopi
        25
    guolaopi  
       2019-10-11 09:32:13 +08:00
    我一直以为 java 跟 C#一样老早就支持这么写了(一脸懵逼
    buckbuckgo
        26
    buckbuckgo  
       2019-10-11 09:36:16 +08:00
    我自己是熟悉的功能性接口(比如 Runnable, OnClickListener)用 lambda 很简洁, 自己也清楚, 不熟悉不常用的仍旧用 匿名对象
    zazalu
        27
    zazalu  
    OP
       2019-10-11 09:36:59 +08:00
    @zzxop 嗯,在看,新手第一次接触 Java 的 FP
    zazalu
        28
    zazalu  
    OP
       2019-10-11 09:38:41 +08:00
    @guolaopi java8 挺早的了!只不过我参与的项目开始用 8 版本是最近的事情 orz,之前一直 1.7
    softtwilight
        29
    softtwilight  
       2019-10-11 09:53:39 +08:00
    刚开始不熟悉正常的,大脑比较讨厌改变,多用用就能感受到好了,函数编程最主要的是对思维模式的影响,你对问题的抽象完全不一样了
    Aresxue
        30
    Aresxue  
       2019-10-11 10:18:14 +08:00
    性能稍差点,但是可读性上升了很多,前提是你习惯函数式编程的思维,毕竟面向对象的编程其实和人类思维的代沟还是差蛮多的
    kansyoukyou
        31
    kansyoukyou  
       2019-10-11 10:57:57 +08:00
    从 Java7 一路写过来的,觉得 Stream 和 FP 结合还是很好用的
    Kamiyu0087
        32
    Kamiyu0087  
       2019-10-11 11:00:58 +08:00
    函数式编程真的大大提高代码可读性
    真心建议啃下来,其实一点不难的
    djFFFFF
        33
    djFFFFF  
       2019-10-11 11:04:32 +08:00
    之前接触过 Python 或者 Scala 的话,沮丧是很正常的。Java 8 的实现一点都不优雅。没接触的话,应该是你还没从 oop 的思路转过来。
    djFFFFF
        34
    djFFFFF  
       2019-10-11 11:06:55 +08:00
    补充一下 @qping 的例子:

    Python 版:
    `[t.getTableName() for t in tables]`

    Scala 版:
    `tables.map(_.getTableName())`
    lihongjie0209
        35
    lihongjie0209  
       2019-10-11 11:13:42 +08:00
    @djFFFFF #34 scala 版本可以使用 vavr 实现。

    python 版本的是列表解析, 不上常规意义上的 map。
    djFFFFF
        36
    djFFFFF  
       2019-10-11 11:18:21 +08:00
    @lihongjie0209 嗯,python 这个是语法糖,不是函数式编程。vavr 看了下感觉还是很笨重
    feelinglucky
        37
    feelinglucky  
       2019-10-11 11:42:30 +08:00
    建议啃下来,真的
    Orenoid
        38
    Orenoid  
       2019-10-11 11:55:54 +08:00
    @djFFFFF #34 python 不是也有 map filter reduce 吗
    lazyfighter
        39
    lazyfighter  
       2019-10-11 11:59:51 +08:00
    我把他称之为,写着舒服看着难受,至于上面的 demo,都是太简单了,你见过上百行的 stream 处理吗,各种类型转换看着头都大了
    by73
        40
    by73  
       2019-10-11 12:04:31 +08:00
    是很鸡肋,连 pattern matching 都没有(逃
    dk7952638
        41
    dk7952638  
       2019-10-11 12:11:14 +08:00
    lambda 很优雅很装逼,但你看过他的实现方式之后你就会发现性能堪忧,想优雅还是直接 scala 吧,java 就不是干这事的
    Narcissu5
        42
    Narcissu5  
       2019-10-11 12:19:19 +08:00
    lambda 好不好,请去看下 lambda 之前无数的匿名类
    taotaodaddy
        43
    taotaodaddy  
       2019-10-11 12:26:49 +08:00 via Android
    java 的 fp 实现堪称是见过的颇不优雅
    @Senventise 还真是。。。我重说

    java 的 fp 实现实在是不优雅呢
    ipwx
        44
    ipwx  
       2019-10-11 13:22:57 +08:00
    去看看 Vert.x 吧。

    函数式对 CRUD 确实可能没啥用。但是对很多高级编程范式,还是很有用的。
    ipwx
        45
    ipwx  
       2019-10-11 13:24:56 +08:00
    @lazyfighter 上百行的 stream,写成普通形式不是要上千,不还是一样不容易懂。

    这种长长长的 stream 不该合理地分块、拆句子、加注释么?

    当然如果你说大部分 Java 程序员写不出容易看懂的 stream,那你的逻辑就和 go 语言那帮拥簇一样了:既然用不好,就砍掉呗(手动斜眼)。我反正不喜欢这样的哲学,我更喜欢 Scala 和 C++ 那种,菜全都端上来了,吃那些,自己看着办。
    optional
        46
    optional  
       2019-10-11 13:40:55 +08:00
    java lambda 最恶心的是 checked exception
    zazalu
        47
    zazalu  
    OP
       2019-10-11 13:49:42 +08:00
    @optional 和这个有什么关系吗?
    yukongzz
        48
    yukongzz  
       2019-10-11 13:49:55 +08:00
    可以看看 kotlin
    optional
        49
    optional  
       2019-10-11 13:57:28 +08:00
    @zazalu 大部分 fp interface 都不带 throw
    sevenstone
        50
    sevenstone  
       2019-10-11 14:09:22 +08:00
    Scala 欢迎你
    zazalu
        51
    zazalu  
    OP
       2019-10-11 14:09:48 +08:00
    @optional 嗯,但是这个自己实现一个不就好了吗(擦汗)
    lazyfighter
        52
    lazyfighter  
       2019-10-11 14:14:49 +08:00
    @ipwx stream 拆开写就不舒服了啊, 本来就是一个句子但是拆开了人为使他易读写着就不舒服了,所以我把它定位为写着爽读着累。另外我的观点是这样的,好比我写代码的时候不写注释,我看代码的时候骂别人不写注释,啊哈哈哈哈哈哈
    aguesuka
        53
    aguesuka  
       2019-10-11 14:29:37 +08:00 via Android
    @zazalu stream 只接受 jdk 的接口作为参数,而且 function 的 default 方法写不写都不好
    slanternsw
        54
    slanternsw  
       2019-10-11 14:37:11 +08:00
    函数式不好 x
    java 的函数式不好 √
    uleh
        55
    uleh  
       2019-10-11 15:33:08 +08:00
    作为一个从 JDK 1.4 用过来的 java 程序员,表示 lambda 真香……
    Cstone
        56
    Cstone  
       2019-10-11 15:40:40 +08:00
    函数式真的很香,没了函数式,没法用流没法链式操作,整个代码都会变得丑陋不堪
    mrobot
        57
    mrobot  
       2019-10-11 16:04:42 +08:00 via iPhone
    个人项目 上上上上真香
    团队项目 确实很怪 最好别写花里胡哨的代码 保重大家都看的懂能跑起来就行 减少沟通成本
    djFFFFF
        58
    djFFFFF  
       2019-10-11 16:12:22 +08:00
    @Orenoid 有但是用的不多,list comprehension 是被认为更 pythonic (可以理解为更有情怀)的方式
    CononYc
        59
    CononYc  
       2019-10-11 16:23:18 +08:00
    一开始我也是这么认为的,函数式看着头大,后面了解多了之后就感觉真香,真好用
    hitsmaxft
        60
    hitsmaxft  
       2019-10-11 17:00:27 +08:00   ❤️ 1
    只是从 api 上改善了对 lambda 支持而已。别提什么函数式。这么去看就释然了。

    1. 支持 lambda 写法的匿名接口实现,不用显示写匿名类实现。(仅限于单个实现方法的接口,多个方法就嗝屁了)
    2. 提供 function 包,这样子第三方有了公共的 api,大伙都可以快乐地玩高阶函数了,不用动不动一堆类。
    3. stream api 改善一些并发和列表处理的延迟计算的实现,基于 1,可以大大减少代码量

    只是提供了函数式的 api/语法糖 ,参考了函数式的一些优点。
    irgil
        61
    irgil  
       2019-10-11 17:01:05 +08:00
    这位亲亲你好,是你的问题呦
    wupher
        62
    wupher  
       2019-10-11 17:49:22 +08:00
    你这要是玩 ReactiveX 还不得吐?

    不过,我现在也更习惯 Groovy 和 Kotlin 风格的 lambda。

    Java 8 的能接受,自己写的时候会绕一下。
    zazalu
        63
    zazalu  
    OP
       2019-10-11 20:33:47 +08:00
    @hitsmaxft 到位,我看下来也觉得是这么回事
    haosamax
        64
    haosamax  
       2019-10-11 20:57:38 +08:00
    @qping 我觉得看起来挺爽的,比 for 明朗多了。看不懂的,是不了解 stream 吧
    islandev
        65
    islandev  
       2019-10-11 21:03:23 +08:00
    function 可以作为参数了 可以传递了 怎么好玩的
    可以精简代码
    ```java
    private Function<T, String> funca;

    private List<Function<T>> funcb

    private String name;

    ```
    bozhongshao
        66
    bozhongshao  
       2019-10-11 21:17:54 +08:00
    @qping 习惯简单不出错的转换 用 lambda 但是很复杂的还是习惯用大括号括住, 慢慢来。
    andyholo
        67
    andyholo  
       2019-10-11 21:33:56 +08:00 via Android
    函数式和面向对象不是对立的吧?怎么听你们说的好像两者有个地位高低似的,复杂逻辑我是不敢用函数式,太复杂耦合度太高了
    kingfalse
        68
    kingfalse  
       2019-10-11 21:50:47 +08:00 via Android
    kotlin 吧,spring boot 支持的很好
    wzdbsss
        69
    wzdbsss  
       2019-10-11 22:07:54 +08:00 via iPhone
    怎么实现 stream 里面的 if else ?最近用 reactor,不知道怎么优雅的 if else
    mskf
        70
    mskf  
       2019-10-11 23:52:39 +08:00
    函数式编程咯,并行运算只需要 parallelStream 就行了
    fuyufjh
        72
    fuyufjh  
       2019-10-12 15:56:47 +08:00
    @godlovesxcjtest 我不知道怎么解释,所以仅仅列出事实:FP 的代表语言 Haskell/Scheme 并不支持类似 Stream API 的东西,而 Stream API 的集大成者 ReactiveX 支持几乎所有语言
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2933 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 00:34 · PVG 08:34 · LAX 16:34 · JFK 19:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.