总览
Devoxx是世界上最大的独立于供应商的Java会议,于11月12日至16日在比利时的Atwerp举行。 今年规模更大了,来自40个不同国家的3400名与会者。 和去年一样 ,我和来自SAP的一小群同事都来了,并享受了很多。
在Nao机器人令人印象深刻的舞蹈和开幕主题演讲之后,超过200场会议探讨了从Java SE到方法论和机器人技术的各种不同技术领域。 对我而言,最有趣的主题之一是JDK 8中Java语言和平台的演变。
我之所以感兴趣,部分原因是我已经开始在Wordcounter上工作,并在另一个并发的Java库Evictor上完成工作,我将在以后的博客中发表有关该库的博客。 在本博客系列中,我想分享一些我参加过的有关此主题的会议的更详细的摘要。 这三个会话都在同一天,同一房间,一个接一个的空间中进行,共同为Java 8中的lambda,并行集合和并行性提供了三种不同的观点。
- 在通往JDK 8的路上:Lambda,并行库等,作者Joe Darcy
- 封闭与收藏–莫里斯·纳夫塔林(Maurice Naftalin)的《八点后的世界》
- Fork / Join,lambda和parallel(): Jose Paumard简化了并行计算(太容易了)
在这篇文章中,我将介绍第一届会议,其他两届会议即将举行。
通往JDK 8的道路上:Lambda,并行库等
在第一届会议上,Oracle多个项目的首席工程师Joe Darcy介绍了JDK 8中所用语言的关键更改,例如lambda表达式和默认方法,总结了实现方法,并研究了并行库及其新特性。编程模型。 本次会议的幻灯片可在此处获得 。
不断发展的Java平台
乔首先谈到了上下文和与语言发展有关的关注。
OpenJDK的一般演变策略是:
- 不要破坏二进制兼容性
- 避免引入源不兼容性。
- 管理行为兼容性更改
上面的列表还扩展到语言的发展。 这些规则意味着将始终识别旧的类文件,限制当前合法代码停止编译的情况,并且还避免了生成的代码中引入行为更改的更改。 该策略的目标是保持现有二进制文件的链接和运行,并保持现有源代码的编译。
这也影响了选择要在语言本身中实现的功能集以及它们的实现方式。 在向Java添加闭包时,此类担忧也有效。 例如,接口是一把双刃剑。 利用我们今天拥有的语言功能,它们无法随着时间的推移而兼容发展。 但是,在现实中,随着人们对API的期望的提高,API逐渐发展。 在语言中添加闭包会导致一个完全不同的编程模型,这意味着如果可以兼容地扩展接口,那将非常有帮助。 这导致更改同时影响语言和VM,称为default methods
。
Lambda项目
Lambda项目引入了协调的语言,库和VM更改。 在该语言中,有lambda expressions
和默认方法。 在库中,有bulk operations on collections
以及对并行性的附加支持。 在虚拟机中,除默认方法外,还对invokedynamic
功能进行了增强。 这是对语言所做的最大更改,比泛型等其他重大更改要大。
什么是lambda表达式?
lambda expression
是一种匿名方法,具有一个参数列表,一个返回类型和一个主体,并且能够从封闭范围引用值:
(Object o) -> o.toString()
(Person p) -> p.getName().equals(name)
除了lambda表达式外,还有method reference
语法:
Object::toString()
lambda的主要好处是它允许程序员将代码视为数据,将其存储在变量中并将其传递给方法。
一些历史
在1995年首次引入Java时,没有多少种语言具有闭包功能,但如今,几乎所有主流语言都使用闭包语言,甚至C ++也是如此。 对于Java来说,获得关闭支持一直是一条漫长而曲折的道路,直到Lambda项目最终于2009年12月开始。目前的状态是JSR 335处于早期草稿审查中 ,有可用的二进制版本 ,并且有望成为很快,JDK 8主线的一部分就会构建。
内部和外部迭代
有两种执行迭代的方法–内部和外部。 在external iteration
您将数据带入代码,而在internal iteration
您将代码带入数据。 外部迭代就是我们今天所拥有的,例如:
for (Shape s : shapes) {if (s.getColor() == RED)s.setColor(BLUE);
}
这种方法有几个局限性。 其中之一是上述循环inherently sequential
上是inherently sequential
,即使没有根本原因不能由多个线程执行,也是如此。
重新编写以对lambda使用内部迭代,上面的代码将是:
shapes.forEach(s -> {if (s.getColor() == RED)s.setColor(BLUE);
})
这不仅是语法上的更改,因为现在该库可以控制迭代的发生方式。 以这种方式编写的代码表达的内容更多what
而更少, how
,如何留给库。 库作者可以自由使用并行性,乱序执行,懒惰和所有其他技术。 这允许库对行为进行抽象,这从根本上是一种更强大的处理方式。
功能介面
Lambda项目避免了添加新类型,而是重用了现有的编码实践。 Java程序员熟悉并长期使用一种方法(如Runnable
, Comparator
或ActionListener
使用接口。 这些接口现在称为functional interfaces
。 还将有新的功能接口,例如Predicate
和Block
。 Lambda表达式求值为功能接口的实例,例如:
PredicateisEmpty = s -> s.isEmpty();
Predicate isEmpty = String::isEmpty;
Runnable r = () -> { System.out.println(“Boo!”) };
因此,现有库与lambda向前兼容,从而导致“自动升级”,从而保持了对这些库的大量投资。
默认方法
上面的示例在Collection
上使用了新方法forEach
。 但是,将方法添加到现有接口在Java中是不行的,因为当客户端在未实现该方法的旧类上调用新方法时,它将导致运行时异常。
default method
是具有实现的接口方法,该实现由VM在链接时编织。 从某种意义上说,这是multiple inheritance
,但是没有理由惊慌,因为这是behavior
多重继承,而不是状态的多重继承。 语法如下所示:
interface Collection<T> {...default void forEach(Block<T> action) {for (T t : this)action.apply(t);}
}
有某些inheritance rules
可以解决多个超类型之间的冲突:
- 规则1 –首选超类方法而不是接口方法(“类获胜”)
- 规则2 –倾向于使用更具体的接口,而不是更少的接口(“子类型获胜”)
- 规则3 –否则,就好像该方法是抽象方法一样。 在默认值冲突的情况下,具体类必须提供一个实现。
总之,通过寻找unique
, most specific default-providing interface
来解决冲突。 有了这些规则,“钻石”就不成问题了。 在最坏的情况下,如果没有唯一,最具体的方法实现,则子类必须提供一个方法,否则会出现编译器错误。 如果此实现需要调用继承的实现之一,则此新语法为A.super.m()
。
默认方法的主要目标是API演变,但它们本身也可用作继承机制。 从中受益的另一种方法是optional methods
。 例如,大多数Iterator
实现都没有提供有用的remove()
,因此可以将其声明为“可选”,如下所示:
interface Iterator<T> {...default void remove() {throw new UnsupportedOperationException();}
}
对集合进行批量操作
对集合执行批量操作还可以实现映射/简化编程风格。 例如,可以通过从shapes
集合中获取stream
,过滤红色元素然后仅对过滤后的元素进行迭代来进一步分解以上代码:
shapes.stream().filter(s -> s.getColor() == RED).forEach(s -> { s.setColor(BLUE); });
上面的代码与您实际想要完成的问题的陈述更为接近。 还有其他有用的批量操作,例如map
, into
或sum
。 该编程模型的主要优点是:
- 更具可组合性
- 清晰–每个阶段都做一件事
- 该库可以使用并行性,乱序,惰性来提高性能等。
stream
是添加到平台的基本的新抽象。 它封装了laziness
,以更好地替代诸如LazyList
类的“惰性”集合。 它是一种允许从其中获取元素序列的工具,其源是集合,数组或函数。 具有流的基本编程模型是管道的模型,例如collection-filter-map-sum
或array-map-sorted-forEach
。 由于流是惰性的,因此它们仅根据需要的元素进行计算,这在filter-map-findFirst
类的情况下会filter-map-findFirst
。
流的另一个优点是,通过让库在后台使用fork / join来简化编程并避免样板,它们允许利用fork / join并行性。
实施技术
在演讲的最后一部分,乔描述了可能的lambda表达式实现技术的优缺点。 考虑了不同的选项,例如内部类和方法句柄,但由于它们的缺点而未被接受。 最好的解决方案是添加一个间接级别,方法是让编译器发出声明性配方(而不是命令性代码)以创建lambda,然后让运行时以它认为合适的方式执行该配方(并确保它是快速的)。
这听起来像是invokedynamic
的工作,它是Java SE 7引入的一种全新的调用模式,其原因完全不同-支持JVM上的动态语言。 事实证明,此功能不再仅适用于动态语言,因为它为lambda提供了合适的实现机制,并且在性能方面也要好得多。
结论
Lambda项目是跨Java语言和平台的大型协调更新。 它为集合提供了更强大的编程模型,并利用了VM中的新功能。 您可以通过下载具有lambda支持的JDK8构建来评估这些新功能。 具有Lambda支持的NetBeans构建和具有Lambda支持的 IntelliJ IDEA 12 EAP构建中也已经提供了IDE 支持 。
我已经在Wordcounter中使用Java中的lambda进行了自己的体验。 正如我已经写过的,我坚信这种编程风格将很快在Java中普及,因此,如果您还没有使用它的经验,我鼓励您尝试一下。
参考: Devoxx 2012:Java 8 Lambda和Parallelism,第1部分,来自我们的JCG合作伙伴 Stoyan Rachev,位于Stoyan Rachev的Blog博客上。
翻译自: https://www.javacodegeeks.com/2012/12/devoxx-2012-java-8-lambda-and-parallelism-part-1.html