我已阅读this question但我对crossinline
关键字有一个更基本的问题。我不确定它解决了什么问题以及如何解决它。
请注意,某些内联函数可能会将传递给它们的lambda作为参数调用,而不是直接来自函数体,而是来自另一个执行上下文,例如本地对象或嵌套函数。在这种情况中,lambdas中也不允许非本地控制流。 要表示,需要使用crossinline修饰符标记lambda参数:
[强调补充]
这句话对我来说不明确。首先,我在实际描绘“此类情况”的含义时遇到了麻烦。我对这个问题有一个大概的概念,但不能提出一个很好的例子。
其次,短语“表明这一点”可以多种方式阅读。说明什么? 特定案例是不允许的?允许 吗?允许(或不允许)给定函数定义中的非本地控制流?
简而言之,我无法弄清楚使用它的上下文是什么,使用它与客户沟通的内容,以及应用此关键字的预期结果是什么。
答案 0 :(得分:15)
首先,我在实际描绘“此类情况”的含义时遇到了麻烦。我对这个问题有一个大概的概念,但不能提出一个很好的例子。
以下是一个例子:
interface SomeInterface {
fun someFunction(): Unit
}
inline fun someInterfaceBy(f: () -> Unit): SomeInterface {
return object : SomeInterface {
override fun someFunction() = f()
// ^^^
// Error: Can't inline 'f' here: it may contain non-local returns.
// Add 'crossinline' modifier to parameter declaration 'f'.
}
}
这里,传递给someInterfaceBy { ... }
的函数在实现SomeInterface
的匿名类中内联。编译someInterfaceBy
的每个调用站点会生成一个新类,其具有不同的someFunction()
实现。
要查看可能出现的问题,请考虑拨打someInterfaceBy { ... }
:
fun foo() {
val i = someInterfaceBy { return }
// do something with `i`
}
在内联lambda中,return
is non-local实际上意味着从foo
返回。但由于lambda未被调用并泄漏到对象i
中,从foo
返回可能绝对没有意义:如果i.someFunction()
(因此lambda)会怎么样?在foo
已经返回后甚至在另一个帖子中调用?
通常,'此类情况'意味着inline
函数调用其功能参数而不是在自己的身体中(有效地,即考虑其他内联函数)但在其他一些函数中声明,就像在非内联lambda和匿名对象中一样。
其次,短语“表明这一点”可以多种方式阅读。说明什么?不允许特定情况?这是允许的吗?允许(或不允许)给定函数定义中的非本地控制流?
这正是我在Kotlin语言设计中修复了上述问题的方法:每当inline
函数打算将其功能参数内联到某个地方时,它就不会被称为就地但是稍后存储和调用,inline
函数的参数应标记为crossinline
,表示在此处传递的lambdas中不允许非本地控制流。
答案 1 :(得分:5)
return
我们先通过一个简单的例子来理解非本地return
的问题:
fun doSomething() {
println("Before lambda")
doSomethingElse {
println("Inside lambda")
return // This is non-local return
}
println("After lambda")
}
inline fun doSomethingElse(lambda: () -> Unit) {
println("Do something else")
lambda()
}
非本地return
在上面的代码中,return
语句被称为非本地返回,因为它不是调用它的函数的本地。这意味着此 return
语句是 doSomething()
函数的本地语句,而不是调用它的 lambda 函数。因此,它终止了当前函数以及最外层函数。
本地return
如果您只想从 lambda 返回,您会说 return@doSomethingElse
。这称为local return,它是指定函数的本地函数。
问题
现在这里的问题是编译器会跳过非本地 return
语句之后的行。 doSomething()
的反编译字节码如下所示:
public static final void doSomething() {
System.out.println("Before lambda");
System.out.println("Doing something else");
System.out.println("Inside lambda");
}
请注意,没有为 println("After lambda")
行生成语句。这是因为我们在 lambda 中有非局部 return
并且编译器认为 return
语句之后的代码毫无意义。
crossinline
关键字crossinline
在这种情况(如上面提到的问题),解决方案是禁止 return
中的非本地 lambda
。为此,我们将 lambda
标记为 crossinline
:
inline fun doSomethingElse(crossinline lambda: () -> Unit) {
println("Doing something else")
lambda()
}
非本地 return
不允许
当您使用 crossinline
关键字时,您是在告诉编译器,“如果我不小心在嵌套函数或本地对象中使用了非本地 return
,请给我一个错误。”:< /p>
fun doSomething() {
println("Before lambda")
doSomethingElse {
println("Inside lambda")
return // Error: non-local return
return@doSomethingElse // OK: local return
}
println("After lambda")
}
现在编译器按预期生成字节码:
public static final void doSomething() {
System.out.println("Before lambda");
System.out.println("Doing something else");
System.out.println("Inside lambda");
System.out.println("After lambda");
}
就是这样!希望我能更容易理解。