为什么Java闭包不能通过返回值之外的方式向外传递值?
程序员文章站
2022-05-05 09:48:01
...
String a; ........(()->a="a"); return a; 为什么不行,是存在技术问题么? 其它语言可以么?c#可以么?莫非从此我要Java一生黑? 至于我为什么问这个问题嘛,就是一个方法有可能有返回值,有可能没有返回值,这就要写两次,还不能同名,写成void和Object两个简直太不优雅。 这个方法其实就是jdbc事务,有查询有不查询,我把事务回滚,异常,日志等写在了一个函数里,这个函数调用这个函数式接口,以后就只要写事务,不用再写回滚,记录日志等操作了。 现在我写了两个方法,感觉很不爽:-(
Java 8语言上的lambda表达式只实现了capture-by-value,也就是说它捕获的局部变量都会拷贝一份到lambda表达式的实体里,然后在lambda表达式里要变也只能变自己的那份拷贝而无法影响外部原本的变量;但是Java语言的设计者又要挂牌坊不明说自己是capture-by-value,为了以后语言能进一步扩展成支持capture-by-reference留下后路,所以现在干脆不允许向捕获的变量赋值,而且可以捕获的也只有“效果上不可变”(effectively final)的参数/局部变量。
关于Java闭包的讨论可以参考我之前的另一个回答:JVM的规范中允许编程语言语义中创建闭包(closure)吗? - RednaxelaFX 的回答
但是Java只是不允许改变被lambda表达式捕获的变量,并没有限制这些变量所指向的对象的状态能不能变。要从lambda表达式向外传值的常见workaround之一就是用长度为1的数组:
JavaScript:也可以
欢迎加入Java黑的大家庭。
改一个field叫不叫传递值? 调用另外的方法叫不叫传递值? Java:如果我有指针或者引用。
指针:怪我了?
引用:…… 你的需求就是多种返回值,那么为什么不考虑使用异常?比修改外部变量来的高明。在开发过程中,其实异常是可以作为逻辑的一部分的。
回复内容:
题主要变通…Java 8语言上的lambda表达式只实现了capture-by-value,也就是说它捕获的局部变量都会拷贝一份到lambda表达式的实体里,然后在lambda表达式里要变也只能变自己的那份拷贝而无法影响外部原本的变量;但是Java语言的设计者又要挂牌坊不明说自己是capture-by-value,为了以后语言能进一步扩展成支持capture-by-reference留下后路,所以现在干脆不允许向捕获的变量赋值,而且可以捕获的也只有“效果上不可变”(effectively final)的参数/局部变量。
关于Java闭包的讨论可以参考我之前的另一个回答:JVM的规范中允许编程语言语义中创建闭包(closure)吗? - RednaxelaFX 的回答
但是Java只是不允许改变被lambda表达式捕获的变量,并没有限制这些变量所指向的对象的状态能不能变。要从lambda表达式向外传值的常见workaround之一就是用长度为1的数组:
String[] a = new String[1];
... ( () -> a[0] = "a" );
return a[0];
/* 你可以用AtomicReference将你所需要的值包装起来 */
private static int sum(int low, int high) {
AtomicReferenceInteger> sum = new AtomicReference(0);
IntStream.rangeClosed(low, high).forEach(i -> sum.set(sum.get() + i));
return sum.get();
}
/* 对于基本类型有专用的包装类更方便使用(且是原子化操作) */
private static int sum2(int low, int high) {
AtomicInteger sum = new AtomicInteger(0);
IntStream.rangeClosed(low, high).forEach(sum::addAndGet);
return sum.get();
}
/* 其实很多时候,我们并不需要改变外部状态不是吗? */
private static int sum3(int low, int high) {
return IntStream.rangeClosed(low, high).sum();
}
C++:可以
C#:可以
F#:可以
VB:可以
补充 @vczhJavaScript:也可以
欢迎加入Java黑的大家庭。
void SomeObject someMethod(SupplierOptionalObject>> foo) {
return foo.get().map(a -> ((Object a) -> ...)).orElseGet(() -> ...);
}
java的函数无法进行引用传递,闭包的捕获也一样。so sad。
什么叫"不能向外传递值"?改一个field叫不叫传递值? 调用另外的方法叫不叫传递值? Java:如果我有指针或者引用。
指针:怪我了?
引用:…… 你的需求就是多种返回值,那么为什么不考虑使用异常?比修改外部变量来的高明。在开发过程中,其实异常是可以作为逻辑的一部分的。
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
相关文章
相关视频
专题推荐
-
独孤九贱-php全栈开发教程
全栈 170W+
主讲:Peter-Zhu 轻松幽默、简短易学,非常适合PHP学习入门
-
玉女心经-web前端开发教程
入门 80W+
主讲:灭绝师太 由浅入深、明快简洁,非常适合前端学习入门
-
天龙八部-实战开发教程
实战 120W+
主讲:西门大官人 思路清晰、严谨规范,适合有一定web编程基础学习
上一篇: 关于遍历字符串的10篇文章推荐
网友评论
文明上网理性发言,请遵守 新闻评论服务协议
我要评论