Val нельзя переназначить, хотя у меня есть var (kotlin)? - PullRequest
1 голос
/ 14 июля 2020

Я сделал свойство расширения var для класса данных следующим образом:

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

Однако компилятор жалуется «Val не может быть переназначен», когда я пытаюсь переназначить «it» новому значению в этой строке :

it = value ?: return@let  

Я не мог понять, почему это происходит ?? У меня свойство растрового изображения «var», а не «val», так в чем проблема? И что еще важнее, какое решение или альтернатива?

Ответы [ 2 ]

2 голосов
/ 14 июля 2020

Переменная внутри лямбда-выражений неизменяема, 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.

Изменить: Свойства расширения не могут иметь вспомогательное поле, расширения - это буквально геттеры и сеттеры.

  • Единственное, что вы можете сделать, это создать карту с уникальный идентификатор, в котором вы n хранить значения, но это может быть невозможно для сбора мусора
  • Еще одна вещь, которую вы можете сделать (что я рекомендую), - это использовать делегирование

Пример делегирования:

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()
0 голосов
/ 14 июля 2020

Для создания настраиваемого сеттера вам необходимо получить доступ к резервному полю свойства от до field, а не it, например:

set(value) =  {
            field = value
        }
...