JAVA8中加入lambda演算是一个令人兴奋的新特性——虽然这个新特性来得太迟了,目前的主流开发语言中,JAVA似乎是最后一个支持函数式思维的语言。

虽然晚了点,但总比没有好——况且我认为它的实现还是可以的,至少比C++的实现好一点(C++编译器不能自动很好的处理闭包环境,却要求程序员在代码中指定要引入到lambda表达式中的变量(捕获列表)——C++的类型系统过于丰富,如果没有捕获列表,则编译器无法得知应该通过“值”捕获还是通过“引用”捕获,而JAVA的类型系统简单很多,没有这个问题)。

以前每当我从Scala中回到Java,就有一种莫名的痛苦——Scala的集合库专为函数式而设计,其抽象程度高于Java的集合库,使得常常Scala两三行代码可以搞定的事情,在Java中要十几行代码才行。

现在使用Java8几个月了(我从beta版的时候就在使用openjdk8了),回到公司再去使用老掉牙的jdk6,也是痛苦得要命——说jdk6老掉牙一点也不过份,从03年jdk5开始到jdk7发布之前,java的语法从来没有变过,而到现在公司使用的最新版本的jdk还是jdk6——虽然Jdk8目前所支持的函数式语法在简洁性上仍然远不Scala,但也算是突飞猛进了。

在jdk8正式版本发布之后,网上关于jdk8的文章开始多了起来,基本上是一片叫好声。我个人也是非常喜欢这些变化,但因为:一来已经有很多人说这些好处了,我没有必要再缀述;二来现在使用jdk8的多半是爱学习的朋友,对于好的地方自然会花时间去体会,也不需要太多的讲。

所以我在这儿多说一点java中可能对于函数式思维还没有准备好的东西,而对其优点,如果有很多人都提过的,我就不在这里说了,如果谈到得比较少的,我就稍微讲一两句,比如这一项就很少有人提到:

新增的Stream接口的非终止运算的实现都是惰性求值的,这个设计在整体上保证了集合运算的运行效率。

在大数据时代,并发能力会成为语言的核心竞争力,而函数式语言开发的并发程序的性能通常都好于命令式语言,这也是为什么java这样的快20年的语言也开始转变了思维的原因之一。

例如:Scala与Java同是运行在JVM上的语言,但Scala开发的Spark相对于Java开发的Hadoop性能有100倍的提高。

下图来自于apache spark项目网站(逻辑回归算法执行时间对比)

这样大的差距是让人难以相信的,但目前来看,这就是事实——同一个JVM,仅仅是语言上的差别就能在性能上有这样的差据么?

我认为Hadoop的性能不如Spark的原因并不是使用Java就写不出那么高性能的东西出来,而是在Java中有一些东西使其在开发高性能的并发程序上存在很大的阻力。

我并没有深入研究Hadoop和Spark的内部机制,所以只就语言本身说一下Java8目前尚存在的(可能的)弱点。

注:以下先用一两句简单列一下,日后再补充。

一、存在空。

null是在正常的java代码中很常见的关键字,但对于函数式的思维中,空是没有意义的。

一个对象为空,就意味着无法调用这个对象的任何方法,而在函数式的程序中,如果某函数无法匹配出一个对象,通常也意味着这个函数结束了。

如果产生空对象还会继续的函数,那通常也意味着这个空引用会被赋予新的对象。

jdk8中也可以看到为避免空而修改的集合框架的方法,如:
Map接口中增加了一个方法getOrDefault来试图规避get方法会返回空的问题;

Jdk8新增的Optional类也是为模拟函数式语言中的模式匹配而增加的——空是不可能进行模式匹配的。

二、存在可变状态。

一个对象,如果是可变的,那就意味着这个对象的并发性有问题。

但java中要使一个类型的对象不可能被改变,是一个很麻烦的事情(想象一下所有属性全部加一个final),而开发一个可变状态的对象却非常容易。

虽然写一个符合纯函数式要求的函数不一定必须使用不可变状态,但如果所有对象都是不可变状态的,则写出来的函数就一定符合函数式的标准。

可变对象通常意味着并发调用这个对象时会出现问题,所以就需要对这个对象的某个部分加锁来规避问题,而锁是使并发性变差的最直接的原因。

三、无尾递归优化。

在Scala和Erlang这样的函数式风格的语言中,尾递归优化已是很平常的概念,但JAVA程序员多数都没有听说过这个概念(因为Java中没有尾递归优化)——关于尾递归优化可以参考我的另一篇文章《对SNL语言的解释器实现尾递归优化》。

函数式风格中的不可变状态的对象对并发性有很大的影响,但对于写惯了命令式语言代码的程序员来说,会产生一个非常不习惯的效应——没有循环。

想像一下:一个语言中没有for也没有while,可能是一个灾难。但如果熟悉了函数式的风格,即便没有循环,我们也可以实现等价的功能。

四、过于考虑向上兼容。

java从jdk5到现在的jdk8,没有增加任何一个保留字,所有的新的语法变化完全体现在代码结构上,很多新思想用库来实现,这大大影响了新思想的使用及推广。

不过这一点和性能的关系不是太大(库设计好了,性能也不差),但也是有点关系的——很多函数式语言中,部分常用的数据结构是语言的语法级别的,而不是库级别的。

五、没有增加新的更先进的线程模型。

前一段时间看到一段对话,是采访Ruby的作者松本行弘的,大概是这么说的:

问:如果时光倒流,你会做什么改变(对Ruby)?

答:去掉线程,使用更先进的基于actor的线程模型。

先列以上几条,以后随着理解的加深,可能会有所增加、删减或修改。

以上几点对于普通的java程序来说,是很平常的,但对于开发并发的程序或应用函数式思维,会不同程度的形成障碍。

再引用我的另一篇文章《用纯函数式思维在Java8下写的一段奇葩程序》,大家可以从这里感受一下Java8的lambda表达式在稍微复杂一点的情况下的表现是多么奇(chou)葩(lou)。

简说JAVA8引入函数式的问题的更多相关文章

  1. 谈一谈Java8的函数式编程(二) --Java8中的流

    流与集合    众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...

  2. [译]Java8的函数式接口

    Java8引入了 java.util.function 包,他包含了函数式接口,具体的描述在以下api说明文档中: 函数式接口为lambda表达式和方法引用提供目标类型.每个函数式接口有一个单独的抽象 ...

  3. 简析JAVA8函数式接口

    一,定义 "有且只有一个抽象方法的接口"----函数式接口的定义. @FunctionalInterface public interface Ifun{ void test(); ...

  4. 谈一谈Java8的函数式编程 (三) --几道关于流的练习题

    为什么要有练习题?    所谓学而不思则罔,思而不学则殆,在系列第一篇就表明我认为写博客,既是分享,也是自己的巩固,我深信"纸上得来终觉浅,绝知此事要躬行"的道理,因此之后的几篇博 ...

  5. JAVA8之函数式接口

    由于JDK8已经发布一段时间了,也开始逐渐稳定,未来使用JAVA语言开发的系统会逐渐升级到JDK8,因为为了以后工作需要,我们有必要了解JAVA8的一些新的特性.JAVA8相对JAVA7最重要的一个突 ...

  6. java8 lambda 函数式编程

    package com.atguigu.java8; import java.util.ArrayList; import java.util.Comparator; import java.util ...

  7. java8的函数式接口

    函数式接口 就是在java8里允许你为一个接口(只有一个实现的,声明为FunctionalInterface注解的)实现一个匿名的对象,大叔感觉它与.net平台的委托很类似,一个方法里允许你接收一个方 ...

  8. Java8自定义函数式编程接口和便捷的引用类的构造器及方法

    什么是函数编程接口? 约束:抽象方法有且只有一个,即不能有多个抽象方法,在接口中覆写Object类中的public方法(如equal),不算是函数式接口的方法. 被@FunctionalInterfa ...

  9. Java8 Functional(函数式接口)

    Functional 函数式(Functional)接口 只包含一个抽象方法的接口,称为函数式接口. 你可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即 ...

随机推荐

  1. sharpdevelop 调整代码字体显示大小

    SharpDevelop中使用ctrl+鼠标滚轮可以调整代码的字体显示大小

  2. apt-pkg

    1 什么是apt-pkg python的apt库,可以做apt可以做的任何事情. 2 apt_pkg.parse_depends(depends, strip_multiarch=True) 这里的d ...

  3. struts2的一些小问题

    1.action和ValueStack的关系2.ValueStack的类set()方法和setValue()方法的区别3.ValueStack的类push()方法的作用4.从ValueStack对象中 ...

  4. SQL外键的作用

    貌似很有用,但没有真正用过: SQL的主键和外键的作用: 外键取值规则:空值或参照的主键值. (1)插入非空值时,如果主键表中没有这个值,则不能插入. (2)更新时,不能改为主键表中没有的值. (3) ...

  5. 【Java报错】Message: 3 字节的 UTF-8 序列的字节 2 无效

    报错logs 2015-03-10 10:15:32,360 ERROR [qtp32195030-27] [InvokeAfterValve.java:55] - javax.xml.stream. ...

  6. js数组清空和去重

    1.splice var ary = [1,2,3,4]; ary.splice(0,ary.length); console.log(ary); // 输出 Array[0],空数组,即被清空了 2 ...

  7. jetty与tomcat

    相同点: 1.tomcat与jetty都是一种servlet引擎,他们都支持标准的servlet规范和javaEE规范 不同点: 1.架构比较 jetty相比tomcat更为简单 jetty架构是基于 ...

  8. mysql17---增量备份

    mysql增量备份: 全备份是: (增量备份一定要看日志的时间和位置节点) mysql数据库会以二进制的形式,把用户对mysql数据库的操作记录到文件中,不用使用定时器了.当用户希望恢复的时候,可以使 ...

  9. java的Date日期使用

    import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; imp ...

  10. JFreeChart教程(二)(转)

    JFreeChart教程(二) 分类: java Component2007-05-31 17:01 8408人阅读 评论(11) 收藏 举报 jfreechartstringplotclassdat ...