Intereting Posts
Преобразует ли файл Kotlin в java, добавив к нему java-код и вернувшись обратно в Kotlin так же, как добавление кода непосредственно в файл Kotlin? Как аргумент Default и @JvmOverloads работают над Kotlin? Когда нам нужны файлы * .meta.js в Котлине? Могу ли я использовать kotlin.reflect, чтобы получить значение поля Kotlin Ktor не может получать данные с данными о местоположении Фрагмент isDetached условный не запускается, хотя фрагмент, безусловно, отсоединен Проверить значение null в функции карты в Котлине Настройка Kotlin в новом проекте Android Studio Kotlin Аннотация: Тип несоответствия: выведенный тип – java.lang.Class <foo>, но java.lang.Class <out jet.Annotation> ожидалось Пользовательское поведение макета Покрытие Jacoco и параметры по умолчанию Kotlin Можно ли получить подкласс класса Kotlin из суперкласса? Что означает тип возврата Void в Котлине Какой законный способ получить аннотации к чистому качеству Котлина через отражение, они всегда отсутствуют? Весенний кеш загрузки не поддерживает kotlin?

Почему блок использования не может безопасно инициализировать var?

Почему это дает ошибку компиляции?

val autoClosable = MyAutoClosable() var myVar: MyType autoClosable.use { myVar= it.foo() } println(myVar) // Error: Variable 'myVar' must be initialized 

Может быть, компилятор просто видит { myVar= it.foo() } как функцию, которая передается другой функции и не знает о том, когда или даже если она будет выполнена?

Но поскольку use – это не просто функция, но замена Котлина на использование Java-ресурса, некоторые специальные знания об этом были бы уместными, не так ли? Прямо сейчас, я вынужден инициализировать myVar с некоторой фиктивной ценностью, которая вовсе не в духе Котлина.

Solutions Collecting From Web of "Почему блок использования не может безопасно инициализировать var?"

Поскольку use { ... } не является языковой конструкцией, а является только библиотечной функцией , компилятор не знает (и в настоящее время не пытается доказать), что пройденная вами лямбда выполняется . Поэтому использование переменной, которая не может быть инициализирована, запрещена.

Например, сравните свой код с этим вызовом функции. Без дополнительного анализа кода они идентичны для компилятора:

 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 как выражение :

 val myVar = try { autoClosable.use { it.foo() } } catch (e: SomeException) { otherValue } 

Теоретически, встроенные функции могут быть проверены, чтобы вызывать lambda ровно один раз, и если компилятор Kotlin может это сделать, это позволит использовать ваш случай использования и некоторые другие. Но это еще не реализовано.

В случае возникновения исключения при выполнении it.foo() , блок use поймает исключение, закроет autoClosable , а затем вернется. В этом случае myVar останется неинициализированным.

Вот почему компилятор не позволит вам делать то, что вы пытаетесь сделать.

Это связано с тем, что use является встроенной функцией , что означает, что тело лямбда будет привязано к функции сайта вызова, а фактический тип переменной myVar зависит от ее контекста.

ЕСЛИ myVar используется в лямбда для чтения, тип – это MyType или его супертип. например:

 // v--- the actual type here is MyType var myVar: MyType = TODO() autoClosable.use { myVar.todo() } 

Если 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 инициализирован или нет. то возникает ошибка компилятора.

Если вы что-нибудь поймаете, код также не может быть скомпилирован, например:

 // 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 в будущем. или kotlin хотят сохранить семантическую последовательность для всех функций.