我有以下代码,这是从 Java程序中的一个实际实现中抽象出来的:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(system.in));
String line;
while ((line = bufferedReader.readLine()) != null) {
    String lineReference = line;
    runLater(() -> consumeString(lineReference));
}

在这里我需要使用lambda表达式的引用副本,当我尝试使用行我得到:

Local variables referenced from a lambda expression must be final or effectively final

对我来说似乎相当尴尬,因为我所做的一切都是为了获得对该对象的新引用,这是编译器本身也可以理解的东西.

所以我会说线在这里是有效的,因为它只在循环中得到赋值,而在其他地方.

任何人都可以对此做出更多的解释,并解释为什么在这里需要它,为什么编译无法解决它?

解决方法

So I would say line is effectively final here,as it only gets the assignment in the loop and Nowhere else.

不,这不是最终的,因为在变量的生命周期中,它在每个循环迭代中被赋予一个新的值.这是完全相反的决赛.

I get: ‘Local variables referenced from a lambda expression must be final or effectively final’. It seems rather awkward to me.

考虑一下:你将lambda传递给runLater(…).当lambda最终执行时,应该使用哪个行的值?当lambda被创建时,它所具有的值,还是当lambda执行时的值?

规则是lambdas(似乎)在lambda执行时使用当前值.他们不会(似乎)创建一个变量的副本.现在,这个规则在实践中如何实现?

>如果line是一个静态字段,那么很容易,因为没有可以捕获lambda的状态.只要需要,lambda就可以读取该字段的当前值,就像其他代码一样.
>如果line是一个实例字段,那也很容易. lambda可以在每个lambda对象的私有隐藏字段中捕获对对象的引用,并通过该对象访问行字段.
>如果line是一个方法中的局部变量(在你的例子中),这突然不容易.在实现级别,lambda表达式is in a completely different method,并且外部代码没有简单的方法可以共享对只存在于该方法中的变量的访问.

为了能够访问局部变量,编译器必须将变量复制到一些隐藏的,可变的持有者对象(例如1元素数组)中,以便可以从包含方法和lambda两者引用持有者对象,给出他们都可以访问变量.

尽管这个解决方案在技术上是有效的,但是由于一系列的原因,它实现的行为是不希望的.分配持有者对象将给局部变量一个不自然的性能特征,从阅读代码看不明显. (仅定义使用局部变量的lambda将使整个方法的变量变得更慢).更糟的是,它会将微妙的竞争条件引入到其他简单的代码中,这取决于执行lambda的时间.在你的例子中,在lambda执行时,可能会发生任何数量的循环迭代,或者该方法可能已经返回,因此该行变量可以具有任何值或没有定义的值,并且几乎肯定不会有值通缉.所以在实践中你仍然需要单独的,不变的lineReference变量!唯一的区别是编译器不需要你这样做,所以它可以让你编写破碎的代码.由于lambda可以最终在不同的线程上执行,这也将为局部变量引入微妙的并发和线程可见性复杂性,这将需要语言来允许局部变量上的volatile修饰符和其他麻烦.

所以,对于lambda来看,当前变化的局部变量的值会引起很多的大惊小怪(而且如果你需要的话也不会有you can do the mutable holder trick manually的优势).相反,该语言对整个kerfuffle表示不要仅仅要求变量是最终(或有效地最终).这样,lambda可以在lambda创建时捕获局部变量的值,并且不需要担心检测到变化,因为它知道不能有任何变化.

This is something the compiler Could also figure out by itself

它确实知道了,这就是为什么它不允许它. lineReference变量对编译器绝对没有任何好处,可以轻松地捕获每个lambda对象创建时在lambda中使用的行的当前值.但是,由于lambda不会检测变量的变化(由于上述原因,这是不切实际的和不可取的),捕获字段和捕获本地人之间的微妙差异将会令人困惑. “最终或有效的最终”规则是为了程序员的好处:它阻止你想知道为什么变量不会出现在lambda中,因为阻止你根本改变它们.以下是没有该规则会发生什么的示例:

String field = "A";
void foo() {
    String local = "A";
    Runnable r = () -> System.out.println(field + local);
    field = "B";
    local = "B";
    r.run(); // output: "BA"
}

如果在lambda中引用的任何局部变量(有效地)为final,那么这个混乱就会消失.

在你的代码中,lineReference是最终的.它的值在其生命周期内被分配一次,在每个循环迭代结束之前超出范围,这就是为什么可以在lambda中使用它.

通过在循环体中声明行,可以有一个可选择的循环:

for (;;) {
    String line = bufferedReader.readLine();
    if (line == null) break;
    runLater(() -> consumeString(line));
}

这是允许的,因为现在行在每个循环迭代结束时超出范围.每次迭代都有一个新的变量,分配一次. (但是,在较低的级别,变量仍然存储在同一个cpu寄存器中,所以不一定要重复地“创建”和“被破坏”,我的意思是,没有额外的费用来声明变量这样的循环,所以没关系.)

注意:所有这一切并不是羊羔的独特之处.它也适用于在方法中以词法方式声明的任何类,其中lambdas从中继承规则.

注2:可以认为,如果他们遵循总是捕获在lambda创建时使用的变量的值的规则,那么lambdas会更简单.那么领域和当地人之间的行为就没有区别,也不需要“最终的或有效的最终”规则,因为在las创建时间之后,lambdas看不到变​​化是很明确的.但这个规则会有自己的丑恶.作为一个示例,对于在lambda中访问的实例字段x,读取x(捕获x的最终值)和this.x(捕获其最终值,看到其字段x改变)的行为之间将存在差异.语言设计很难.

java – 为什么在这种情况下不能引用lambda内的变量?的更多相关文章

  1. ios – 如何从变量访问属性或方法?

    是否可以使用变量作为Swift中方法或属性的名称来访问方法或属性?在PHP中,您可以使用$object->{$variable}.例如编辑:这是我正在使用的实际代码:解决方法你可以做到,但不能使用“纯粹的”Swift.Swift的重点是防止这种危险的动态属性访问.你必须使用Cocoa的Key-ValueCoding功能:非常方便,它完全穿过你要穿过的字符串到属性名称的桥,但要注意:这里是龙.

  2. iOS >>块>>更改块外部的变量值

    我不是在处理一个Object并改变它,就像我的mString一样.我希望’center’属性的行为类似于myInt,因为它是直接访问的C结构,而不是指向对象的指针.我希望’backgroundColor’的行为类似于我的imstring,因为它是一个指向一个新对象的对象的指针,不是吗?

  3. ios – Xcode Bot:如何在post触发器脚本上获得.ipa路径?

    我正在使用机器人来存档iOS应用程序,我需要获取.ipa产品路径才能将其发布到我们的分发系统中.机器人设置:并使用脚本打印所有env变量,其中不包含ipa文件的路径.此外,一些变量指向不存在的目录,即:XCS_OUTPUT_DIR这里的env变量输出:除此之外,我还能够确认.ipa文件是在另一个文件夹中创建的(/IntegrationAssets//

  4. ios – 使用附加字符串本地化Info.plist变量

    我正在尝试本地化应用程序的名称,同时仍然能够根据构建配置追加字符串.所以目前它被设置为:该设置定义为:通过这种方式,我们可以为应用程序添加后缀以用于不同的beta版本.问题是,当我们尝试本地化本地化的InfoPlist.strings中的应用程序显示名称时,就像这样我们覆盖存储在Info.plist中的值,并丢失后缀字符.这有什么好办法吗?

  5. iOS – 开始iOS教程 – 变量之前的下划线?

    这是正确的还是我做错了什么?

  6. ios – 静态计算变量被多次实例化

    我有一个日期格式化程序,我试图在UITableViewCell子类中创建一个单例,所以我创建了一个这样的计算属性:问题是我不止一次看到print语句,这意味着它不止一次被创建.我已经找到了其他方法,但我很想知道这里发生了什么.有任何想法吗?解决方法您的代码段相当于只获取属性,基本上它与以下内容相同:如果你只想运行一次,你应该像定义一个惰性属性一样定义它:

  7. ios – UIApplication.delegate必须仅在主线程中使用[复制]

    我应该在主调度中的viewControllers中声明这些)变量位置声明定义了它的范围.您需要确定这些变量的范围.您可以将它们声明为项目或应用程序级别(全局),类级别或特定此功能级别.如果要在其他ViewControllers中使用这些变量,则使用公共/开放/内部访问控制将其声明为全局或类级别.

  8. ios – 无法理解Objective-C块文档

    为什么localVariable“按价值使用?”>如果我在第二个例子中将__block存储类型添加到localVariable,我错误地假设该块关闭了变量,所以它将它保留在堆中直到块被释放?解决方法Howexactlyisoneexample“accessedbyreference”whiletheotheroneisaccessedbyvariable?self是当前正在执行找到块的方法的对象.强引用只是意味着对象的保留计数增加.IfIaddthe__blockstoragetypetolocalVar

  9. ios – 为BOOL变量编写getter和setter

    显然,使用obj-c,通常没有理由编写getter和setter(感谢有用的mr@synthesize).所以现在,需要做到这一点,我遇到了一个我不知道如何编写它们的问题.:p我敢肯定我可能不会以正确的方式解决我的问题–只是将我的对象子类化得更容易–但我正在尝试编写类别代码以添加属性,因为(在开头)它更快,因为我想学习如何在我的应用程序中使用类别代码.我有这个:我在setter中没有if查询就试过

  10. ios – 为什么不保留__block变量(在非ARC环境中)?

    我正在阅读__blockvariables上的文档,并考虑我使用__block的情况.对我来说,似乎我需要两种情况:>在块中使用时将变量标记为读写>在块内引用self时避免保留周期从表面上看,这两件事似乎并不相关.我认为__block变量没有被保留为更多的技巧我需要记住避免保留周期的特定用例.我想知道,为什么不能保留它们是否有更重要的建筑理由?

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部