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)} 

其中viewgetView() 。 在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(R.id.textView) }对于Fragment ,使用任何Viewtypes创建finalvariables是没有意义的,因为您将丢失连接与onDestroyView之后的视图。

PS使用Kotlin合成属性访问“ Activity和“ Fragment视图。

我相信没有这样的事情“初始化”一次。 variables是最终的或不是。 lateinitvariables不是最终的。 因为,在初始化阶段之后,你重新分配了一些其他的值。

如果有人find解决方案,我正在主演这个问题