Kotlin:val,或者可以设置一次的var
只是好奇:在Kotlin,我很想得到一些可以被懒惰初始化的val,但是有一个参数。 那是因为我需要一些很晚才创建的东西来初始化它。
具体来说,我希望我有:
private lateinit val controlObj:SomeView
要么:
private val controlObj:SomeView by lazy { view:View->view.findViewById(...)}
接着:
override fun onCreateView(....) { val view = inflate(....) controlObj = view.findViewById(...)
或者在controlObj.initWith(view)
情况下controlObj.initWith(view)
或类似的东西:
return view
我不能by lazy
使用by lazy
因为by lazy
不会接受初始化时要使用的外部参数。 在这个例子中 – 包含的view
。
当然,我有lateinit var
但如果我能确保它只是在设置后才能读取,我可以在一行中完成。
是否有一个非常干净的方法来创建一个只读的variables,只初始化一次,但只有当其他一些variables出生? 任何init once
关键字? 在init之后,编译器知道它是不可变的?
我意识到潜在的并发问题,但如果我敢于在init之前访问它,我当然应该被抛出 。
你可以像这样实现自己的委托:
class InitOnceProperty : ReadWriteProperty { 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 (value != EMPTY) { throw IllegalStateException("Value is initialized") } this.value = value } }
之后,你可以使用它如下:
inline fun initOnce(): ReadWriteProperty = 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 } }
如果在初始化之前尝试访问值,则会在您尝试重新分配新值时收到exception。
在这个解决方案中,你实现了一个自定义的委托,它成为你的类的一个单独的属性。 委托里面有一个var
,但controlObj
属性有你想要的保证。
class X { private val initOnce = InitOnce() 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 { 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") }
顺便说一句,如果你需要线程安全,解决方案只是稍有不同:
class InitOnceThreadSafe { private val viewRef = AtomicReference () 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") }
你可以使用lazy
。 例如用TextView
val text by lazy{view?.findViewById(R.id.text_view)}
其中view
是getView()
。 在onCreateView()
您可以使用text
作为只读variables
如果你真的只想设置一个variables,你可以使用单例模式:
companion object { @Volatile private var INSTANCE: SomeViewSingleton? = null fun getInstance(context: Context): SomeViewSingleton = INSTANCE ?: synchronized(this) { INSTANCE ?: buildSomeViewSingleton(context).also { INSTANCE = it } } private fun buildSomeViewSingleton(context: Context) = SomeViewSingleton(context) }
然后,你所要做的就是调用getInstance(...)
,你总是会得到相同的对象。
如果要将对象的生命周期绑定到周围的对象,只需删除伴随对象,然后将初始化器放在类中。
同步块也处理并发问题。
对于Activity
来说,可以这么做: private val textView: TextView by lazy { findViewById
对于Fragment
,使用任何View
types创建final
variables是没有意义的,因为您将丢失连接与onDestroyView
之后的视图。
PS使用Kotlin合成属性访问“ Activity
和“ Fragment
视图。
我相信没有这样的事情“初始化”一次。 variables是最终的或不是。 lateinitvariables不是最终的。 因为,在初始化阶段之后,你重新分配了一些其他的值。
如果有人find解决方案,我正在主演这个问题