匿名 Kotlin 侦听器中未解析的引用
Unresolved reference inside anonymous Kotlin listener
我有下面的代码。它是科特林。知道为什么 textToSpeech.setLanguage(Locale.UK)
中的 textToSpeech
告诉我们没有为 textToSpeech
解析的引用吗?
val textToSpeech = TextToSpeech(
applicationContext,
object : TextToSpeech.OnInitListener {
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK)
}
}
})
一开始以为是Idea kotlin plugin的bug,结果好像真的编译不了
Kotlin 强化了变量初始化策略,现在禁止在其初始化器中引用变量,即使在 lambda 和对象表达式中,这似乎是合理的:想象一下在变量赋值之前立即调用 lambda。
对于你的情况,我可以建议在这个相当麻烦的结构中使用 object expression 作为解决方法:
val textToSpeech = object {
val value: TextToSpeech get() = inner
private val inner = TextToSpeech(
applicationContext,
{ value.setLanguage(Locale.UK) }
)
}.value
这将通过value
属性 初始化一个内部带有inner
的匿名对象。请注意,inner
初始化程序使用 value
属性。然后value
提取出来就可以使用了
但是请记住这个技巧是不安全的:在运行时,在分配 inner
之前使用 value
(例如在 TextToSpeech
构造函数中)将抛出 NullPointerException
.
此外,我已将 OnInitListener
替换为使用 SAM conversion 的 lambda 来缩短,但仍然可以在那里使用对象表达式。
UPD: 检查 this question 我为推广这种方法所做的努力。使用它,你可以写
val textToSpeech = selfReference {
TextToSpeech(
applicationContext,
{ self.setLanguage(Locale.UK) }
)
}
这是解决该问题的一种非常易读且清晰的方法。首先你应该定义这个:
fun <T> selfReferenced(initializer: () -> T) = initializer.invoke()
operator fun<T> T.getValue(any: Any?, property: KProperty<*>) = this
以后使用
val valueName: ValueType by selfReferenced{
//here you can create and use the valueName object
}
以你的问题为例,你可以这样做:
val textToSpeech:TextToSpeech by selfReferenced {
TextToSpeech(
applicationContext,
TextToSpeech.OnInitListener { status ->
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK)
}
})
}
在 selfReferenced 块内,您可以不受限制地使用外部对象。您唯一应该注意的是显式声明类型以避免递归类型检查问题。
我有下面的代码。它是科特林。知道为什么 textToSpeech.setLanguage(Locale.UK)
中的 textToSpeech
告诉我们没有为 textToSpeech
解析的引用吗?
val textToSpeech = TextToSpeech(
applicationContext,
object : TextToSpeech.OnInitListener {
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK)
}
}
})
一开始以为是Idea kotlin plugin的bug,结果好像真的编译不了
Kotlin 强化了变量初始化策略,现在禁止在其初始化器中引用变量,即使在 lambda 和对象表达式中,这似乎是合理的:想象一下在变量赋值之前立即调用 lambda。
对于你的情况,我可以建议在这个相当麻烦的结构中使用 object expression 作为解决方法:
val textToSpeech = object {
val value: TextToSpeech get() = inner
private val inner = TextToSpeech(
applicationContext,
{ value.setLanguage(Locale.UK) }
)
}.value
这将通过value
属性 初始化一个内部带有inner
的匿名对象。请注意,inner
初始化程序使用 value
属性。然后value
提取出来就可以使用了
但是请记住这个技巧是不安全的:在运行时,在分配 inner
之前使用 value
(例如在 TextToSpeech
构造函数中)将抛出 NullPointerException
.
此外,我已将 OnInitListener
替换为使用 SAM conversion 的 lambda 来缩短,但仍然可以在那里使用对象表达式。
UPD: 检查 this question 我为推广这种方法所做的努力。使用它,你可以写
val textToSpeech = selfReference {
TextToSpeech(
applicationContext,
{ self.setLanguage(Locale.UK) }
)
}
这是解决该问题的一种非常易读且清晰的方法。首先你应该定义这个:
fun <T> selfReferenced(initializer: () -> T) = initializer.invoke()
operator fun<T> T.getValue(any: Any?, property: KProperty<*>) = this
以后使用
val valueName: ValueType by selfReferenced{
//here you can create and use the valueName object
}
以你的问题为例,你可以这样做:
val textToSpeech:TextToSpeech by selfReferenced {
TextToSpeech(
applicationContext,
TextToSpeech.OnInitListener { status ->
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK)
}
})
}
在 selfReferenced 块内,您可以不受限制地使用外部对象。您唯一应该注意的是显式声明类型以避免递归类型检查问题。