Why does this give a compile error?
val autoClosable = MyAutoClosable()
var myVar: MyType
autoClosable.use {
myVar= it.foo()
}
println(myVar) // Error: Variable 'myVar' must be initialized
Maybe the compiler just sees { myVar= it.foo() }
as a function that is passed to another function and has no knowledge about when or even if it will be executed?
But since use
is not just a function but Kotlin's replacement for Java's try-with-resource, some special knowledge about it would be appropriate, wouldn't it? Right now, I am forced to initialize myVar
with some dummy value, which is not in the spirit of Kotlin at all.
Since
use { ... }
is not a language construct but is just a library function, the compiler doesn't know (and, currently, does not make effort to prove) that the lambda you pass is ever executed. Therefore the usage of the variable that might not be initialized is prohibited.For example, compare your code to this function call. Without additional code analysis, they are identical for the compiler:
To bypass this restriction, you can use the value returned from
use
(this is the value your block returns) to initialize your variable:And if you also want to handle the exception it might throw, then use
try
as an expression:Theoretically, inline functions can actually be checked to invoke a lambda exactly once, and if the Kotlin compiler could do that, it would allow your use case and some others. But this has not been implemented yet.
In case an exception occurs while executing
it.foo()
, theuse
block will catch the exception, close yourautoClosable
, and then return. In this case,myVar
will be left uninitialized.This is why the compiler won't let you do what you're trying to do.
This is because
use
is an inline function, which means the lambda body will be inlined to the call-site function, and the actual type of the variablemyVar
depends on its context.IF the
myVar
is used in lambda for reading, the the type isMyType
or its supertype. for example:IF the
myVar
is used in lambda for writing, the actual type is anObjectRef
. why? this is because Java don't allow you change the variable out of the annoymous class scope. In fact,myVar
is effectively-final. for example:So when the compiler checking at
println(myVar)
, it can't sure the element of theObjectRef
is initialized or not. then a compiler error is raised.IF you catch anything, the code also can't be compiled, for example:
But when the actual type of the
myVar
isMyType
, it works fine. for example:Why kotlin didn't optimize inline functions to use
MyType
directly for writing?the only thing I think, the compiler don't know the
myVar
whether is used in lambda body of another uninline function in future. or kotlin want to keep semantic consistent for all of the functions.