lambda捕获this_非捕获Lambda的实例

lambda捕获this

大约一个月前,我在Java 8的lambda表达式框架下总结了Brian Goetz的观点 。 目前,我正在研究有关默认方法的文章,令我惊讶的是,我又回到了Java处理lambda表达式的方式。 这两个功能的交集可能会产生微妙但令人惊讶的效果,我想讨论一下。

总览

为了使这一点更有趣,我将以一个示例开头,该示例将以我的个人WTF达到顶峰 时刻。 完整的示例可以在专用的GitHub项目中找到 。

然后,我们将看到有关此意外行为的解释,并最终得出一些结论以防止错误。

这里有个例子……它不是那么简单或抽象,因为我希望它显示这种情况的相关性。 但是从某种意义上说,这仍然是一个示例,它仅暗示实际上可能有用的代码。

功能界面

假设对于在构建过程中已经存在结果的场景,我们需要对Future接口进行特殊化。

我们决定通过创建一个接口ImmediateFuture来实现此目的,该接口get()使用默认方法实现除get()之外的所有功能。 这导致功能界面 。

您可以在此处查看源代码。

一个工厂

接下来,我们实现FutureFactory 。 它可能会创建各种期货,但肯定会创建我们的新子类型。 它是这样的:

未来工厂

/*** Creates a new future with the default result.*/
public static Future<Integer> createWithDefaultResult() {ImmediateFuture<Integer> immediateFuture = () -> 0;return immediateFuture;
}/*** Creates a new future with the specified result.*/
public static Future<Integer> createWithResult(Integer result) {ImmediateFuture<Integer> immediateFuture = () -> result;return immediateFuture;
}

创造未来

最后,我们使用工厂创建一些期货并将其收集在一组中:

创建实例

public static void main(String[] args) {Set<Future<?>> futures = new HashSet<>();futures.add(FutureFactory.createWithDefaultResult());futures.add(FutureFactory.createWithDefaultResult());futures.add(FutureFactory.createWithResult(42));futures.add(FutureFactory.createWithResult(63));System.out.println(futures.size());
}

WTF ?!

运行程序。 控制台会说...

4? 不。 3。

WTF ?!

Lambda表达式的评估

那么这是怎么回事? 那么,与有关lambda表达式的评估一些背景知识,这其实并不奇怪。 如果您不太熟悉Java的实现方式,那么现在是赶上Java的好时机。 做到这一点的一种方法是观看Brian Goetz的演讲“ Java中的Lambdas:深入了解”或阅读我的摘要 。

实例非捕获lambdas

Lambda表达式的实例

理解这种行为的关键在于,事实是JRE不保证如何将lambda表达式转换为相应接口的实例。 让我们看一下Java语言规范对此事的看法:

15.27.4。 Lambda表达式的运行时评估

[…]

分配并初始化具有以下属性的类的新实例,或者引用具有以下属性的类的现有实例。

[…类的属性–这里不足为奇…]

这些规则旨在通过以下方式为Java编程语言的实现提供灵活性:

  • 不必在每次评估中分配一个新对象。
  • 由不同的lambda表达式产生的对象不必属于不同的类(例如,如果主体相同)。
  • 评估产生的每个对象不必属于同一类(例如,可以内联捕获的局部变量)。
  • 如果“现有实例”可用,则不需要在先前的lambda评估中创建它(例如,可能在封闭类的初始化期间分配了它)。
[…]

JLS,Java SE 8版,§15.27.4

在其他优化中,这显然使JRE可以返回相同的实例,以重复评估lambda表达式。

非捕获Lambda表达式的实例

请注意,在上面的示例中,表达式不捕获任何变量。 因此,它永远不会因评估而改变。 而且由于lambda并非设计为具有状态,因此不同的评估在其生命周期中也无法“分散”。 因此,一般而言,没有充分的理由来创建多个不捕获的lambda实例,因为它们在整个生命周期中都完全相同。 这样可以使优化始终返回相同的实例。

(将其与捕获某些变量的lambda表达式进行对比。对此表达式的直接评估是创建一个将捕获的变量作为字段的类。每个评估都必须创建一个新实例,将捕获的变量存储在其字段中这些情况显然并不普遍。)

这就是上面代码中发生的事情。 () -> 0是一个不捕获的lambda表达式,因此每个评估都返回相同的实例。 因此,对createWithDefaultResult()每次调用都是如此。

但是,请记住,这仅适用于当前安装在我的计算机上的JRE版本(用于Win 64的Oracle 1.8.0_25-b18)。 您的可以有所不同,下一个gal也可以如此等等。

得到教训

因此,我们了解了为什么会这样。 尽管这很有意义,但我仍然会说这种行为并不明显,因此并不是每个开发人员都期望的。 这是漏洞的滋生地,因此让我们尝试分析情况并从中学习一些东西。

使用默认方法进行子类型化

可以说,意外行为的根本原因是如何完善Future的决定。 为此,我们扩展了另一个接口,并使用默认方法实现了部分功能。 仅剩一个未实现的方法, ImmediateFuture成为了一个启用lambda表达式的功能接口。

另外, ImmediateFuture可以是抽象类。 这样可以防止工厂意外返回相同的实例,因为它不能使用lambda表达式。

关于抽象类和默认方法的讨论不容易解决,因此在这里我不尝试这样做。 但是,我很快将发布有关默认方法的文章,并且我打算再讲一遍。 可以说,在做出决定时应考虑此处提出的案例。

工厂中的Lambda

由于lambda的引用相等性不可预测,因此工厂方法应仔细考虑使用它们来创建实例。 除非方法的协定明确允许不同的调用返回相同的实例,否则应完全避免使用它们。

我建议在此禁令中包括捕获lambda。 (对我而言)一点也不清楚,在什么情况下可以在将来的JRE版本中重用同一实例。 一种可能的情况是,JIT发现紧密的循环创建了总是(或至少经常)返回同一实例的供应商。 通过用于不捕获lambda的逻辑,重用同一供应商实例将是有效的优化。

匿名类与Lambda表达式

注意匿名类和lambda表达式的不同语义。 前者保证创建新实例,而后者则不能。 为了继续该示例,以下createWithDefaultResult()将导致futures –大小为4的集合:

匿名类的替代实现

public static Future<Integer> createWithDefaultResult() {ImmediateFuture<Integer> immediateFuture = new ImmediateFuture<Integer>() {@Overridepublic Integer get() throws InterruptedException, ExecutionException {return 0;}};return immediateFuture;
}

这尤其令人不安,因为许多IDE允许从匿名接口实现到lambda表达式的自动转换,反之亦然。 由于两者之间存在细微的差异,这种看似纯粹的句法转换会带来细微的行为变化。 (我最初并不了解。)

万一您遇到了这种情况,并选择使用匿名类,请确保以明显方式记录您的决定! 不幸的是,似乎没有办法阻止Eclipse对其进行任何转换(例如,如果将转换作为保存操作启用),这也会删除匿名类中的所有注释。

最终的替代方案似乎是一个(静态)嵌套类。 我知道没有哪个IDE敢将其转换为lambda表达式,因此这是最安全的方法。 尽管如此,仍需要对其进行文档记录,以防止下一个Java-8迷(确实像您一样)出现并加紧您的仔细考虑。

功能接口标识

当您依赖功能接口的标识时要小心。 始终考虑是否有可能,无论您在何处获得这些实例,都可能反复将您交给同一个实例。

但这当然是模糊的,几乎没有什么具体的结果。 首先,所有其他接口都可以简化为功能接口。 这实际上就是我选择Future的原因-我想举一个不会立即尖叫疯狂的LAMBDA狗屎的例子 其次,这会使您很快变得偏执。

因此,请不要过分考虑-记住这一点。

保证行为

最后但并非最不重要的一点(这始终是正确的,但值得在此重复):

不要依靠无证的行为!

JLS不保证每个lambda评估都返回一个新实例(如上面的代码所示)。 但是它不能保证观察到的行为,即非捕获的lambda总是由同一实例表示。 因此,不要编写依赖于两者的代码。

不过,我必须承认,这是一个艰难的过程。 认真地说,谁在使用某些功能之前先看过它们的JLS? 我当然不会。

反射

我们已经看到Java不能保证所评估的lambda表达式的身份。 尽管这是一个有效的优化,但它可能会产生令人惊讶的效果。 为了防止这种情况引入细微的错误,我们导出了以下准则:

  • 使用默认方法部分实现接口时要小心。
  • 不要在工厂方法中使用lambda表达式。
  • 当身份很重要时,请使用匿名类或更好的内部类。
  • 依赖功能接口的标识时要小心。
  • 最后, 不要依赖未记录的行为!

翻译自: https://www.javacodegeeks.com/2015/01/instances-of-non-capturing-lambdas.html

lambda捕获this

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338972.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql8 安装_MySQL8.x安装使用

1.下载网址https://dev.mysql.com/downloads/mysql/下载要登录&#xff0c;可以使用Oracle账户登录2.安装MySQL服务下载好的解压到D:developer目录下配置MySQL(windows下是my.ini&#xff0c;Linux下是my.cnf)[mysql]# 设置mysql客户端默认字符集default-character-setutf8[mys…

从原理到方法,一文讲清如何应对C语言内存泄露!

可能不少开发者都遇到过内存泄漏导致的网上问题&#xff0c;具体表现为单板在现网运行数月以后&#xff0c;因为内存耗尽而导致单板复位现象。一方面&#xff0c;内存泄漏问题属于比较浅显的错误&#xff0c;此类问题遗漏到现网&#xff0c;影响不好&#xff1b;另一方面&#…

openshift_OpenShift上具有NetBeans的Java EE

openshift今天是慕尼黑的NetBeans日 。 我很高兴提出一个关于将Red Hat产品与我著名的IDE集成的会议。 因此&#xff0c;我一直在谈论WildFly &#xff0c; EAP &#xff0c;Git和OpenShift Online&#xff0c;并展示了使用该工具集优化开发工作流程的所有不同方式。 大约有10…

const char * 类型的实参与 char * 类型的形参不兼容_4 种 C++ 强制类型转换,你都清楚吗?...

我们先来回忆以下&#xff0c;C 语言的强制类型转换形式&#xff1a;(type) expr;这种旧式强制类型转换从表现形式上来说不够清晰明了&#xff0c;容易看漏&#xff0c;一旦转换过程出现问题&#xff0c;追踪起来也就更加困难。为了解决以上问题&#xff0c;C不仅兼容了C的强制…

ggplot2中显示坐标轴_R可视化08|ggplot2图层标度图层(scale layer)图例篇

"pythonic生物人"的第106篇分享本文详细介绍ggplot2中图例标度(legends scales)&#xff0c;续前篇R可视化07|ggplot2图层-标度图层(scale layer)-颜色盘篇本文目录4、图例标度(legends scale)图例位置设置修改ggplot2的图例符号ggplot2的图例顺序|方向等花里胡哨设置…

C explicit 关键字详解

explicit关键字的作用explicit关键字在写程序时使用的次数较少,但是仔细观察会发现,在C 标准库中的相关类声明中explicit出现的频率是很高的,那么explicit关键字到底有什么作用呢?接下来我就为大家一一解答。explicit为清晰的;明确的之意.顾名思义,关键字explicit可以阻止隐式…

python决策树算法_决策树算法及python实现

决策树算法是机器学习中的经典算法 1.决策树(decision tree) 决策树是一种树形结构&#xff0c;其中每个内部节点表示一个属性上的测试&#xff0c;每个分支代表一个测试输出&#xff0c;每个叶节点代表一种类别。 假设小明去看电影&#xff0c;影响看电影的外部因素有 时间 电…

长见识:你真的知道C语言里extern quot;Cquot; 的作用吗?

经常在C语言的头文件中看到下面的代码&#xff1a;#ifdef __cplusplus extern "C" { #endif// all of your legacy C code here#ifdef __cplusplus } #endif这通常用于C 和C混合编程的时候&#xff0c;为了防止C 的编译器在编译C文件的时候出现错误&#xff1b;众所周…

python自动批量发邮件脚本_Python实现自动发送邮件功能

简单邮件传输协议(SMTP)是一种协议&#xff0c;用于在邮件服务器之间发送电子邮件和路由电子邮件。Python提供smtplib模块&#xff0c;该模块定义了一个SMTP客户端会话对象&#xff0c;可用于使用SMTP或ESMTP侦听器守护程序向任何互联网机器发送邮件。 SMTP通讯的基本流程可以概…

C语言中#if,#if defined ,#ifdef,extern的用法描述

1、#if 和#ifdef当asd_eee表达式存在而且&#xff0c;值为ture的时候接续向下执行例如#define TARGET_LITTLE_ENDINA 1 #define TARGET_BIG_ENDINA 0 #ifdef TARGET_LITTLE_ENDINA call little endina function #else call big endina function #endif上面的今天写的代码&…

delphi报列表索引越界怎么处理_图解Elasticsearch索引机制,此篇带你领悟新世界...

前言随着Elastic的上市&#xff0c;ELK不仅在互联网大公司得到长足的发展&#xff0c;而且在各个中小公司都得到非常广泛的应用&#xff0c;甚至连"婚庆网站"都开始使用Elasticsearch了。随之而来的是 Elasticsearch 相关部署、框架、性能优化的文章早已铺天盖地。因…

为什么C语言函数不能返回数组,却可以返回结构体

C语言函数为什么不能返回数组&#xff1f;在C语言程序开发中&#xff0c;我们不可以编写下面这样的代码&#xff1a;char f(void[8]{ char ret;// ...fill... return ret; }int main(int argc, char ** argv) {char obj_a[10];obj_a f(); }不可以编写这样的代码这其实就是不能…

C语言printf()函数具体解释和安全隐患

程序员都知道&#xff0c;也都会使用printf函数&#xff0c;但你知道它也有“安全隐患”吗&#xff1f;下面就来举例我说说&#xff1a;嵌入式专栏1问题描述打印输出的数据并不是理论值&#xff0c;如下图&#xff08;右边&#xff09;&#xff1a;嵌入式专栏2进一步描述问题请…

java map 如何根据key获得对象_ThreadLocal:Java中的影分身

关于ThreadLocal&#xff0c;你有哪些疑问&#xff1f;ThreadLocal是用来解决什么问题的&#xff1f;如何使用ThreadLocal&#xff1f;ThreadLocal的实现原理是什么&#xff1f;可否举几个实际项目中使用ThreadLocal的案例&#xff1f;基础知识ThreadLocal是线程局部变量&#…

【C语言】你可能对 sizeof() 有点误解。。。

各位&#xff0c;今天还是按照惯例给大家分享一个C语言容易出现的小错误&#xff0c;这也是跟sizeof有关的&#xff0c;问题虽小&#xff0c;却可管中窥豹&#xff0c;话不多说&#xff0c;代码先行&#xff1a;#include int main() { int i; i 8; printf("%d\…

nginx tcp转发_Nginx学习(九):负载均衡服务

介绍对于请求而言&#xff0c;负载均衡能很好的均摊请求&#xff0c;提高服务端吞吐率和整体性能&#xff0c;多个服务节点部署的方式&#xff0c;也提高了容灾和服务高可用。一、负载均衡分类负载均衡分为&#xff1a;GSLB和SLB。1. GDLB全局负载均衡&#xff0c;往往按照国家…

控制台发送get命令_.NET Core使用命令行参数库构建控制台应用程序

前言在我们开发中可能需要设计一次性应用程序&#xff0c;这些实用程序可以利用接近原始源代码的优势&#xff0c;但可以在与主Web应用程序完全独立的安全性上下文中启动。具体在 [管理过程](https://12factor.net/admin-processes)中也已经列出了原因。创建控制台应用打开命令…

C语言函数为什么不能返回数组?

C语言函数为什么不能返回数组&#xff1f;在C语言程序开发中&#xff0c;我们不可以编写下面这样的代码&#xff1a;char f(void)[8] {char ret; // ...fill... return ret; }int main(int argc, char ** argv) {char obj_a[10]; obj_a f(); }不可以编写这样的代码这其实就是不…

mockito_书评:Mockito Essentials

mockitoSujoy Acharya的Mockito Essentials副标题&#xff08; Packt出版 &#xff0c;2014年10月&#xff09;是&#xff1a;“实用指南&#xff0c;可帮助您使用Mockito进行单元测试并开始运行。” Mockito Essentials中的前言和七章涵盖大约190个实质性页面。 前言 在序言中…

C语言中quot;##quot;的独特用法

市面上有很多比较火的编程语言&#xff0c;比如Python、 JAVA、 Go等&#xff0c;你可能觉得C语言很古老、很落后。如果你有这种想法&#xff0c;那可能你只是一个初学的菜鸟。可能绝大部分 C 程序员都不知道"##"隐藏用法&#xff0c;下面就来给大家讲讲。一、##的“…