Emi*_*l D 25 java lambda java-8 method-reference
我正在学习Java 8,我遇到了一些我觉得有点奇怪的东西.
请考虑以下代码段:
private MyDaoClass myDao;
public void storeRelationships(Set<Relationship<ClassA, ClassB>> relationships) {
RelationshipTransformer transformer = new RelationshipTransformerImpl();
myDao.createRelationships(
relationships.stream()
.map((input) -> transformer.transformRelationship(input))
.collect(Collectors.toSet())
);
}
Run Code Online (Sandbox Code Playgroud)
基本上,我需要将调用的输入集映射relationships到不同的类型,以符合我正在使用的DAO的API.对于转换,我想使用RelationshipTransformerImpl我实例化为局部变量的现有类.
现在,这是我的问题:
如果我要修改上面的代码如下:
public void storeRelationships(Set<Relationship<ClassA, ClassB>> relationships) {
RelationshipTransformer transformer = new RelationshipTransformerImpl();
myDao.createRelationships(
relationships.stream()
.map((input) -> transformer.transformRelationship(input))
.collect(Collectors.toSet())
);
transformer = null; //setting the value of an effectively final variable
}
Run Code Online (Sandbox Code Playgroud)
我显然会得到一个编译错误,因为局部变量transformer不再是"有效的最终".但是,如果用方法引用替换lambda:
public void storeRelationships(Set<Relationship<ClassA, ClassB>> relationships) {
RelationshipTransformer transformer = new RelationshipTransformerImpl();
myDao.createRelationships(
relationships.stream()
.map(transformer::transformRelationship)
.collect(Collectors.toSet())
);
transformer = null; //setting the value of an effectively final variable
}
Run Code Online (Sandbox Code Playgroud)
然后我不再收到编译错误!为什么会这样?我认为编写lambda表达式的两种方法应该是等价的,但显然还有更多的事情要发生.
Era*_*ran 22
JLS 15.13.5可以解释:
方法参考表达式评估的时间比lambda表达式(第15.27.4节)更复杂.当方法引用表达式在:: separator之前具有表达式(而不是类型)时,将立即计算该子表达式.存储评估结果,直到调用相应功能接口类型的方法为止; 此时,结果将用作调用的目标引用.这意味着:: separator之前的表达式仅在程序遇到方法引用表达式时计算,并且不会在函数接口类型的后续调用中重新计算.
据我了解,因为在你的情况下transformer是:: separator之前的表达式,它只被评估一次并存储.由于不必为了调用引用的方法而重新评估它,transformer因此稍后将其指定为null 并不重要.
疯狂的猜测,但对我来说,这是发生了什么......
编译器无法声明创建的流完全是同步的; 它认为这是一种可能的情况:
relationships参数创建流;transformer;编译时生成的是一个调用站点; 它仅在流展开时链接.
在第一个lambda中,您引用局部变量,但此变量不是调用站点的一部分.
在第二个lambda中,由于您使用方法引用,这意味着生成的调用站点必须保留对该方法的引用,因此保存该方法的类实例.事实上它是由你后来改变的局部变量引用的并不重要.
我的两分钱......
在第一个示例中,transformer每次调用映射函数时都会引用,因此每次关系都会引用一次.
在第二个示例中transformer仅引用一次,transformer::transformRelationship传递给map().所以如果之后发生变化并不重要.
这些不是 "编写lambda表达式的两种方法",而是lambda表达式和方法引用,这是该语言的两个不同特征.
| 归档时间: |
|
| 查看次数: |
1539 次 |
| 最近记录: |