我已经阅读了这个问题,但我对这个crossinline关键字有一个更基本的问题.我不确定它解决了什么问题以及如何解决它.
来自Kotlin Docs,
请注意,某些内联函数可能会将传递给它们的lambda作为参数调用,而不是直接来自函数体,而是来自另一个执行上下文,例如本地对象或嵌套函数.在这种情况下,lambda中也不允许非本地控制流.为了表明这一点,lambda参数需要用crossinline修饰符标记:
[强调补充]
这句话对我来说很模糊.首先,我在实际描绘"此类情况"的含义时遇到了麻烦.我对这个问题有一个大概的概念,但不能提出一个很好的例子.
其次,短语"表明这一点"可以多种方式阅读.说明什么?这一个特殊情况是不允许的?这是允许的吗?允许(或不允许)给定函数定义中的非本地控制流?
简而言之,我很难弄清楚使用它的上下文是什么,使用它与客户端通信的内容,以及应用此关键字的预期结果是什么.
hot*_*key 16
首先,我在实际描绘"此类情况"的含义时遇到了麻烦.我对这个问题有一个大概的概念,但不能提出一个很好的例子.
这是一个例子:
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'.
}
}
Run Code Online (Sandbox Code Playgroud)
这里,传递给的函数someInterfaceBy { ... }内联在一个匿名类实现中SomeInterface.每个调用站点的编译someInterfaceBy产生一个具有不同实现的新类someFunction().
要查看可能出现的问题,请考虑致电someInterfaceBy { ... }:
fun foo() {
val i = someInterfaceBy { return }
// do something with `i`
}
Run Code Online (Sandbox Code Playgroud)
内联lambda内部return是非本地的,实际上意味着从中返回foo.但是由于lambda没有被调用并且泄漏到对象中i,因此返回foo可能绝对没有意义:如果i.someFunction()(之后是lambda)调用之后foo已经返回甚至是在不同的线程中,该怎么办?
一般来说,"这种情况"意味着inline调用其功能参数的函数不是在它们自己的实体中(有效地,即考虑其他内联函数),而是在它们声明的其他函数内部,例如非内联lambda和匿名对象.
其次,短语"表明这一点"可以多种方式阅读.说明什么?不允许特定情况?这是允许的吗?允许(或不允许)给定函数定义中的非本地控制流?
这正是我在Kotlin语言设计中修复上述问题的方法:每当一个inline函数打算将其功能参数内联到某个地方时,它可能不是就地调用但是后来存储和调用,inline应该标记该函数的参数as crossinline,表示在这里传递的lambdas中不允许非本地控制流.
return我们先return通过一个简单的例子来理解non-local的问题:
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()
}
Run Code Online (Sandbox Code Playgroud)
非本地 return
在上面的代码中,该return语句被称为非本地返回,因为它不是调用它的函数的本地。这意味着该return语句是doSomething()函数的本地语句,而不是调用它的 lambda 函数。因此,它终止当前函数以及最外层函数。
当地的 return
如果你只是想从 lambda 返回,你会说return@doSomethingElse. 这称为本地返回,它是指定它的函数的本地返回。
问题
现在这里的问题是编译器跳过了非本地return语句之后的行。反编译的字节码doSomething()如下所示:
public static final void doSomething() {
System.out.println("Before lambda");
System.out.println("Doing something else");
System.out.println("Inside lambda");
}
Run Code Online (Sandbox Code Playgroud)
请注意,没有为该行生成语句println("After lambda")。这是因为我们return在 lambda 中使用了 non-local并且编译器认为return语句之后的代码毫无意义。
crossinline关键字crossinline
在这样的情况下(如问题如上所述),所述溶液是不允许的非本地return内部lambda。为了实现这一点,我们将其标记lambda为crossinline:
inline fun doSomethingElse(crossinline lambda: () -> Unit) {
println("Doing something else")
lambda()
}
Run Code Online (Sandbox Code Playgroud)
非本地return不允许
当您使用crossinline关键字时,您是在告诉编译器,“如果我不小心return在嵌套函数或本地对象中使用了非本地对象,请给我一个错误。”:
fun doSomething() {
println("Before lambda")
doSomethingElse {
println("Inside lambda")
return // Error: non-local return
return@doSomethingElse // OK: local return
}
println("After lambda")
}
Run Code Online (Sandbox Code Playgroud)
现在编译器按预期生成字节码:
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");
}
Run Code Online (Sandbox Code Playgroud)
就是这样!希望我让它更容易理解。
| 归档时间: |
|
| 查看次数: |
1336 次 |
| 最近记录: |