【问题标题】:Val cannot be reassigned although I have a var (kotlin)?尽管我有一个 var (kotlin),但无法重新分配 Val?
【发布时间】:2020-11-04 00:02:10
【问题描述】:

我为数据类创建了一个 var 扩展属性,如下所示:

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

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

it = value ?: return@let  

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

【问题讨论】:

    标签: android kotlin var kotlin-android-extensions


    【解决方案1】:

    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,更改也不会反映到实际变量中。

    编辑:扩展属性不能有支持字段,扩展实际上是 getter 和 setter。

    • 您可以做的一件事是制作一个带有唯一标识符的 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()
    

    【讨论】:

    • 很好的解释!!谢谢。我知道“字段”关键字但是当我尝试按照您所说的那样进行制作时,编译器再次抱怨“未解析的引用:字段”。你知道这个错误吗?
    • 当我尝试创建一个普通的 var 属性而不是扩展时,这个错误也只发生在扩展属性上,它工作正常。编译器不再抱怨字段关键字
    • 非常感谢!!,解决了我的问题并节省了我的时间
    【解决方案2】:

    为了创建自定义setter,您需要通过field而不是it访问property的支持字段,如下所示:

    set(value) =  {
                field = value
            }
    

    【讨论】:

    • 我试过了,编译器说“Unresolved reference: field”
    • 哦,我刚刚注意到它是一个扩展属性。扩展属性不能有支持字段。您需要以其他方式对其进行建模
    猜你喜欢
    • 2022-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 2021-09-04
    • 2019-12-03
    • 2021-10-22
    • 1970-01-01
    相关资源
    最近更新 更多