编辑(2017 年 2 月 19 日):我收到了非常全面的reply https://discuss.kotlinlang.org/t/lambdas-and-implicit-references-to-the-instance-of-the-enclosing-class/2288/2?u=storix迈克·赫恩 (Mike Hearn) 关于此问题的评论:
与 Java 一样,Kotlin 中发生的情况在不同情况下会有所不同。
- 如果 lambda 被传递给内联函数并且没有标记为 noinline,那么整个事情就会消失,并且没有额外的类或
对象被创建。
- 如果 lambda 没有捕获,那么它将作为单例类发出,其实例被一次又一次地重用(一个类+一个对象
分配)。
- 如果 lambda 捕获,则每次使用 lambda 时都会创建一个新对象。
因此,除了内联情况外,它的行为与 Java 类似
哪里更便宜。这种高效的 lambda 编码方法
这是 Kotlin 中的函数式编程更具吸引力的原因之一
比在Java中。
编辑(2017 年 2 月 17 日):我已在 中发布了有关此主题的问题Kotlin 讨论 https://discuss.kotlinlang.org/t/lambdas-and-implicit-references-to-the-instance-of-the-enclosing-class/2288。也许 Kotlin 工程师会带来一些新东西。
kotlin lambda 是否只是简单地转换为 Java 匿名函数?
我自己问这个问题(这里一个简单的更正:这些被称为匿名类,而不是函数)。文中并没有明确的答案Koltin
文档。他们只是state https://kotlinlang.org/docs/reference/inline-functions.html that
使用高阶函数会带来一定的运行时间损失:每个
函数是一个对象,它捕获一个闭包,即那些变量
在函数体中访问。
他们的意思有点令人困惑在函数体中访问的变量。对封闭类实例的引用也被计算在内吗?
我已经看到您在问题中引用的主题,但目前看来它已经过时了。我找到了更多最新信息here https://discuss.kotlinlang.org/t/lambda-expression-or-anonymous-function-keep-an-implicit-reference-of-the-enclosing-class/1429:
Lambda 表达式或匿名函数保留隐式引用的
封闭类
因此,不幸的是,Kotlin 的 lambda 表达式似乎与 Java 的匿名内部类存在相同的问题。
为什么匿名内部类不好?
来自Java
specs https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html:
关联类 O 的直接内部类 C 的实例 i
与 O 的实例,称为直接封闭实例
我。对象的直接封闭实例(如果有)是
对象创建时确定
这意味着匿名类总是有一个implicit对封闭类的实例的引用。由于引用是隐式的,因此无法摆脱它。
看看这个简单的例子
public class YourActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new Runnable() {
// the inner class will keep the implicit reference to the outer activity
@Override
public void run() {
// long-running task
}
}).start();
}
}
正如您所看到的,在这种情况下,在执行长时间运行的任务之前,将会出现内存泄漏。解决此问题的一种方法是使用静态嵌套类。
Since Kotlin's
非内联lambda 保留对封闭类实例的引用,它们在内存泄漏方面也有类似的问题。
奖励:与其他 Lambda 实现的快速比较
Java 8 Lambda 表达式
Syntax:
-
声明SAM(单一抽象方法)接口
interface Runnable { void run(); }
-
使用此接口作为 lambda 的类型
public void canTakeLambda(Runnable r) { ... }
-
通过你的 lambda
canTakeLambda(() -> System.out.println("Do work in lambda..."));
内存泄漏问题:如中所述specs http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html:
对此的引用——包括通过
不合格的字段引用或方法调用——是,
本质上,是对最终局部变量的引用。拉姆达机构
包含此类引用捕获此的适当实例。在
其他情况,不保留对此的引用通过对象。
简而言之,如果您不使用封闭类中的任何字段/方法,则不会隐式引用this
就像匿名类的情况一样。
逆转录酶
来自docs https://github.com/orfjackal/retrolambda
Lambda 表达式通过将其转换为匿名来向后移植
内部类。这包括使用的优化单例
无状态 lambda 的实例避免重复对象的表达式
分配。
我想,这是不言自明的。
苹果的斯威夫特
Syntax:
-
声明与 Kotlin 类似,在 Swift 中 lambda 称为闭包:
func someFunctionThatTakesAClosure(closure: (String) -> Void) {}
-
通过关闭
someFunctionThatTakesAClosure { print($0) }
Here, $0
参考闭包的第一个String
争论。这对应于it
在科特林中。注意:与 Kotlin 不同,在 Swift 中我们还可以引用其他参数,例如$1
, $2
etc.
内存泄漏问题:
在 Swift 中,就像在 Java 8 中一样,闭包捕获了对self
(this
在 Java 和 Kotlin 中)仅当它访问实例的属性时,例如self.someProperty
,或者如果闭包调用实例上的方法,例如self.someMethod()
.
开发人员还可以轻松指定他们只想捕获弱引用:
someFunctionThatTakesAClosure { [weak self] in print($0) }
我希望 Kotlin 也能做到这一点:)