为什么会出现编译错误?
val autoClosable = MyAutoClosable()
var myVar: MyType
autoClosable.use {
myVar= it.foo()
}
println(myVar) // Error: Variable 'myVar' must be initialized
也许编译器只看到{ myVar= it.foo() }
作为传递给另一个函数的函数,并且不知道何时甚至是否会被执行?
但由于use
不仅仅是一个函数,而且Kotlin取代了Java的try-with-resource,因此对它的一些特殊知识是合适的,不是吗?现在,我被迫用一些虚拟值初始化myVar
,这根本不符合Kotlin的精神。
答案 0 :(得分:6)
由于use { ... }
不是语言构造而是is just a library function,编译器不知道(并且,目前,不会努力证明)你传递的lambda是曾经执行过。因此,禁止使用可能未初始化的变量。
例如,将您的代码与此函数调用进行比较。如果没有额外的代码分析,它们对于编译器来说是相同的:
inline fun ignoreBlock(block: () -> Unit) = Unit
var myVar: MyType
ignoreBlock { myVar = it.foo() }
println(myVar) // Expectedly, `myVar` stays uninitialized, and the compiler prohibits it
要绕过此限制,您可以使用从use
返回的值(这是您的块返回的值)来初始化变量:
val myVar = autoClosable.use {
it.foo()
}
如果您还想处理它可能抛出的异常,请使用try
as an expression:
val myVar = try {
autoClosable.use {
it.foo()
}
} catch (e: SomeException) {
otherValue
}
理论上,实际上可以检查内联函数以仅调用一次lambda,如果Kotlin编译器可以这样做,它将允许你的用例和其他一些。但这尚未实施。
答案 1 :(得分:-1)
如果在执行it.foo()
时发生异常,use
块将捕获异常,关闭autoClosable
,然后返回。在这种情况下,myVar
将保持未初始化状态。
这就是为什么编译器不会让你做你想做的事情。
答案 2 :(得分:-1)
这是因为use
是一个内联函数,这意味着lambda body将内联到call-site函数,以及变量的实际类型myVar
取决于其背景。
IF myVar
在lambda中用于读取,类型为MyType
或其超类型。例如:
// v--- the actual type here is MyType
var myVar: MyType = TODO()
autoClosable.use {
myVar.todo()
}
IF myVar
在lambda中用于写入,实际类型为ObjectRef
。为什么?这是因为Java不允许您将变量更改为烦人的类范围。事实上,myVar
有效最终。例如:
// v--- the actual type here is an ObjectRef type.
var myVar: MyType
autoClosable.use {
myVar = autoClosable.foo()
}
所以当编译器在println(myVar)
检查时,它无法确定ObjectRef
的元素是否已初始化。然后引发编译器错误。
IF 你抓住任何东西,代码也无法编译,例如:
// v--- the actual type here is an ObjectRef type.
var myVar: MyType
try {
autoClosable.use {
myVar = it.foo()
}
} catch(e: Throwable) {
myVar = MyType()
}
// v--- Error: Variable 'myVar' must be initialized
println(myVar)
但是当myVar
的实际类型为MyType
时,它可以正常工作。例如:
var myVar: MyType
try {
TODO()
} catch(e: Throwable) {
myVar = MyType()
}
println(myVar) // works fine
为什么 kotlin没有优化内联函数来直接使用MyType
进行编写?
我认为唯一的一点是,编译器不知道myVar
是否会在未来的另一个uninline函数的lambda体中使用。或者kotlin希望保持所有功能的语义一致。