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. Android 封装实现各种样式对话框

    先上图 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/disso ...

  2. 【iOS系列】-文件管理

    OC中操作文件,需要使用NSFileManager: 需要使用NSFileManager的创建方式: //单例模式创建对象 NSFileManager * f2 = [NSFileManager de ...

  3. 《C++ Primer Plus》学习笔记3

    <C++ Primer Plus>学习笔记3 第8章 函数探幽 ============================================================== ...

  4. android.view.View

    * This class represents the basic building block for user interface components. A View * occupies a ...

  5. HDU 5544 Ba Gua Zhen dfs+高斯消元

    Ba Gua Zhen Problem Description During the Three-Kingdom period, there was a general named Xun Lu wh ...

  6. Java之jdk命令行工具详解

    JPS---虚拟机进程状况工具 常用的参数: -l 输出Java应用程序的main class的完整包 -q 仅显示pid,不显示其它任何相关信息 -m 输出传递给main方法的参数 -v 输出传递给 ...

  7. Lightoj 1025 - The Specials Menu

    区间dp /* *********************************************** Author :guanjun Created Time :2016/6/30 23:2 ...

  8. java代码实现JDBC连接MySql以及引用驱动程序包

    JDBC链接MySql     JDBC链接MySql的话题已经老掉牙了,这次我只想通过使用简洁的代码实现,采用封装的思想,将链接MySql的代码封装在类的静态方法中,供一次性调用返回java.sql ...

  9. codeforces 682C C. Alyona and the Tree(dfs)

    题目链接: C. Alyona and the Tree time limit per test 1 second memory limit per test 256 megabytes input ...

  10. 【SCOI 2005】 最大子矩阵

    [题目链接] 点击打开链接 [算法] 动态规划 我们发现,M只有两种取值,1和2,那么,只需分类讨论即可 当M = 1时,其实这个问题就成了就最大连续子段和的问题,只不过要选K段而已 用f[i][j] ...