尽管我有一个变种(kotlin),但不能重新分配Val?

时间:2020-07-14 15:36:43

标签: android kotlin var kotlin-android-extensions

我对数据类做了一个var扩展属性,如下所示:

var Element.bitmap: Bitmap?
        get() = downloadImg(PLACE_HOLDER_URL) //Default
        set(value) = this.bitmap.let {
            it = value ?: return@let
        }

但是,当我尝试在此行中将“ it”重新分配给新值时,编译器抱怨“ Val无法重新分配”:

it = value ?: return@let  

我真不明白为什么会这样??我有一个“ var”位图属性,而不是“ val”,那是什么问题?更重要的是什么是解决方案或替代方案?

2 个答案:

答案 0 :(得分:2)

lambda内部的变量是不可变的,it有变量bitmap的浅表副本。

您应该使用field分配到后备字段(实际变量)。

set(value) {
    value?.let { field = it }
}

let函数创建变量的浅表副本,因此,如果另一个线程更改了可见的可变变量(var),则可以安全地使用它,而不会冒突变的危险。

示例:

class Test {
    var prop: Int? = 5
}

fun main() {
    val test = Test()
    thread {
        Thread.sleep(100)
        test.prop = null
    }
    if (test.prop != null) {
        Thread.sleep(300)  // pretend we did something
        println(test.prop)  // prints null even inside if check
    }
}

为解决这些情况,使用了浅拷贝,例如let,它传递了这些不变的浅拷贝。

class Test {
    var prop: Int? = 5
}

fun main() {
    val test = Test()
    thread {
        Thread.sleep(100)
        test.prop = null
    }
    test.prop?.let {  // `it` is a shallow copy, changes won't be reflected
        Thread.sleep(300)  // pretend we did something
        println(it)  // prints 5
    }
}

结论:it本身并不是实际变量,因此即使您能够为it分配某些内容,更改也不会反映到实际变量中。

编辑:扩展程序属性没有后备字段,扩展程序实际上是获取器和设置器。

  • 您可以做的一件事是制作一个具有唯一标识符的Map,可以在其中存储值,但是可能无法对其进行垃圾收集
  • 您可以做的另一件事(我建议)是使用委托

委托示例:

class ElementBitmapDelegate {
    private var value: Bitmap? = null
    
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Bitmap {
        return value ?: downloadImg(PLACE_HOLDER_URL).also { setValue(thisRef, property, it) }
        // return value or if value is null return from downloadImg() and set it to value
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, v: Bitmap?) {
        v?.let { value = it }  // if v is not null then set value to v
    }
}


var Element.bitmap: Bitmap? by ElementBitmapDelegate()

答案 1 :(得分:0)

要创建自定义设置器,您需要通过field而不是it访问property的后备字段,如下所示:

set(value) =  {
            field = value
        }