Man*_*eko 18 android immutability lazy-evaluation kotlin kotlin-lateinit
只是好奇:在Kotlin中,我很想得到一些可以通过懒惰来初始化的val,但是有一个参数.那是因为我需要为了初始化它而创建的东西很晚.
具体来说,我希望我有:
private lateinit val controlObj:SomeView
Run Code Online (Sandbox Code Playgroud)
要么:
private val controlObj:SomeView by lazy { view:View->view.findViewById(...)}
Run Code Online (Sandbox Code Playgroud)
然后:
override fun onCreateView(....) {
val view = inflate(....)
controlObj = view.findViewById(...)
Run Code Online (Sandbox Code Playgroud)
或在第二种情况下controlObj.initWith(view)
或类似的情况:
return view
Run Code Online (Sandbox Code Playgroud)
我无法使用,by lazy
因为by lazy
初始化时不会接受外部参数.在这个例子中 - 包含view
.
当然我有,lateinit var
但如果我能确保它在设置后变为只读,我会在一行中完成它会很好.
是否有一种非常干净的方法来创建只读初始化一次的只读变量,但只有当其他变量出生时?任何init once
关键字?在init之后,编译器知道它是不可变的?
我知道这里存在潜在的并发问题,但如果我敢于在init之前访问它,我当然应该被抛出.
hlu*_*kyi 14
您可以像这样实现自己的委托:
class InitOnceProperty<T> : ReadWriteProperty<Any, T> {
private object EMPTY
private var value: Any? = EMPTY
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (value == EMPTY) {
throw IllegalStateException("Value isn't initialized")
} else {
return value as T
}
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
if (this.value != EMPTY) {
throw IllegalStateException("Value is initialized")
}
this.value = value
}
}
Run Code Online (Sandbox Code Playgroud)
之后,您可以按以下方式使用它:
inline fun <reified T> initOnce(): ReadWriteProperty<Any, T> = InitOnceProperty()
class Test {
var property: String by initOnce()
fun readValueFailure() {
val data = property //Value isn't initialized, exception is thrown
}
fun writeValueTwice() {
property = "Test1"
property = "Test2" //Exception is thrown, value already initalized
}
fun readWriteCorrect() {
property = "Test"
val data1 = property
val data2 = property //Exception isn't thrown, everything is correct
}
}
Run Code Online (Sandbox Code Playgroud)
如果您在初始化之前尝试访问值,则会出现异常以及尝试重新分配新值时。
在此解决方案中,您实现了一个自定义委托,它成为您类中的一个单独属性。委托有一个var
内部,但该controlObj
属性具有您想要的保证。
class X {
private val initOnce = InitOnce<View>()
private val controlObj: View by initOnce
fun readWithoutInit() {
println(controlObj)
}
fun readWithInit() {
initOnce.initWith(createView())
println(controlObj)
}
fun doubleInit() {
initOnce.initWith(createView())
initOnce.initWith(createView())
println(controlObj)
}
}
fun createView(): View = TODO()
class InitOnce<T : Any> {
private var value: T? = null
fun initWith(value: T) {
if (this.value != null) {
throw IllegalStateException("Already initialized")
}
this.value = value
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T =
value ?: throw IllegalStateException("Not initialized")
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果您需要线程安全,则解决方案略有不同:
class InitOnceThreadSafe<T : Any> {
private val viewRef = AtomicReference<T>()
fun initWith(value: T) {
if (!viewRef.compareAndSet(null, value)) {
throw IllegalStateException("Already initialized")
}
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T =
viewRef.get() ?: throw IllegalStateException("Not initialized")
}
Run Code Online (Sandbox Code Playgroud)
您可以使用lazy
。例如与TextView
val text by lazy<TextView?>{view?.findViewById(R.id.text_view)}
Run Code Online (Sandbox Code Playgroud)
哪里。view
getView()
之后onCreateView()
您可以用作text
只读变量
归档时间: |
|
查看次数: |
6845 次 |
最近记录: |