Home  >  Article  >  Backend Development  >  为什么Java闭包不能通过返回值之外的方式向外传递值?

为什么Java闭包不能通过返回值之外的方式向外传递值?

WBOY
WBOYOriginal
2016-06-06 16:22:411319browse

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的数组:
<code class="language-text">String[] a = new String[1];
... ( () -> a[0] = "a" );
return a[0];
</code>
<code class="language-java"><span class="cm">/* 你可以用AtomicReference将你所需要的值包装起来 */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">sum</span><span class="o">(</span><span class="kt">int</span> <span class="n">low</span><span class="o">,</span> <span class="kt">int</span> <span class="n">high</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">AtomicReference</span><span class="o"><span class="n">Integer</span><span class="o">></span> <span class="n">sum</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AtomicReference</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
    <span class="n">IntStream</span><span class="o">.</span><span class="na">rangeClosed</span><span class="o">(</span><span class="n">low</span><span class="o">,</span> <span class="n">high</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">i</span> <span class="o">-></span> <span class="n">sum</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">sum</span><span class="o">.</span><span class="na">get</span><span class="o">()</span> <span class="o">+</span> <span class="n">i</span><span class="o">));</span>
    <span class="k">return</span> <span class="n">sum</span><span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="o">}</span>

<span class="cm">/* 对于基本类型有专用的包装类更方便使用(且是原子化操作) */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">sum2</span><span class="o">(</span><span class="kt">int</span> <span class="n">low</span><span class="o">,</span> <span class="kt">int</span> <span class="n">high</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">AtomicInteger</span> <span class="n">sum</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AtomicInteger</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
    <span class="n">IntStream</span><span class="o">.</span><span class="na">rangeClosed</span><span class="o">(</span><span class="n">low</span><span class="o">,</span> <span class="n">high</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">sum</span><span class="o">::</span><span class="n">addAndGet</span><span class="o">);</span>
    <span class="k">return</span> <span class="n">sum</span><span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="o">}</span>

<span class="cm">/* 其实很多时候,我们并不需要改变外部状态不是吗? */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">sum3</span><span class="o">(</span><span class="kt">int</span> <span class="n">low</span><span class="o">,</span> <span class="kt">int</span> <span class="n">high</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">IntStream</span><span class="o">.</span><span class="na">rangeClosed</span><span class="o">(</span><span class="n">low</span><span class="o">,</span> <span class="n">high</span><span class="o">).</span><span class="na">sum</span><span class="o">();</span>
<span class="o">}</span>
</span></code>

C++:可以

C#:可以

F#:可以

VB:可以

补充 @vczh
JavaScript:也可以

欢迎加入Java黑的大家庭。
<code class="language-java"><span class="kt">void</span> <span class="n">SomeObject</span> <span class="nf">someMethod</span><span class="o">(</span><span class="n">Supplier</span><span class="o"><span class="n">Optional</span><span class="o"><span class="n">Object</span><span class="o">>></span> <span class="n">foo</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">foo</span><span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">map</span><span class="o">(</span><span class="n">a</span> <span class="o">-></span> <span class="o">((</span><span class="n">Object</span> <span class="n">a</span><span class="o">)</span> <span class="o">-></span> <span class="o">...)).</span><span class="na">orElseGet</span><span class="o">(()</span> <span class="o">-></span> <span class="o">...);</span>
<span class="o">}</span>
</span></span></code>
java的函数无法进行引用传递,闭包的捕获也一样。so sad。 什么叫"不能向外传递值"?

改一个field叫不叫传递值? 调用另外的方法叫不叫传递值? Java:如果我有指针或者引用。
指针:怪我了?
引用:…… 你的需求就是多种返回值,那么为什么不考虑使用异常?比修改外部变量来的高明。在开发过程中,其实异常是可以作为逻辑的一部分的。
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn