简体   繁体   English

更改继承自具有不可变值的密封 class 的 class 中的可变值

[英]Changing a mutable value in a class inheriting from a sealed class with an immutable value

In Kotlin, I'm trying to have a mutable generic value in sealed class A, and a mutable generic Number value in sealed class B with a mutable Long/... value in final C/...;在 Kotlin 中,我试图在密封的 class A 中有一个可变的通用值,在密封的 class B 中有一个可变的通用数值,在最终 C 中有一个可变的 Long/... 值but whenever I change A and B's values to "var" it gives me: Var-property type is T, which is not a type of overridden public open var value: Number .但是每当我将 A 和 B 的值更改为“var”时,它都会给我: Var-property type is T, which is not a type of overridden public open var value: Number

Example:例子:

sealed class Data<T>(
    open val name: String,
    open var value: T
)

sealed class NumberData<T: Number>(
    override val name: String,
    override var value: T
): Data<Number>(name, value)

class TextData(name: String, override var value: String) : Data<String>(name, value)
class LongData(name: String, override var value: Long) : NumberData<Long>(name, value)
// class ...Data(name: String, value: ...) : Data<...>(name, value)

fun main() {
    val dataSet = setOf<Data<*>>(TextData("a1", "hello, world"), LongData("a2", 50024))
    val translate = "a1 a2 c3"

    // Goal: Translate "a1 a2 c3" into a string using dataSet values while ignoring those that aren't in the set.
    // "a1 a2 c3" -> "a1's value a2's value"

    buildSet {
        val map = dataSet.associateBy { it.name }
        for(name in translate.split(" ")) {
            if(map.containsKey(name)) {
                add(map[name]!!.apply { value = 5 }) // "val" cannot be reassigned
                // cannot change val to var in Data as NumberData will give a compiler error:
                // Var-property type is T, which is not a type of overridden public open var value: Number
            }
        }
    }.joinToString(" ") { it.value.toString() }
}

Why is this, and how would I go about fixing it?为什么会这样,我将如何修复它?

NumberData<T> inherits from Data<Number> , not Data<T> , so the type of value is expected to be Number , not T . NumberData<T>继承自Data<Number> ,而不是Data<T> ,因此value的类型应为Number ,而不是T

It is possible to override a read-only property with a subtype, eg可以使用子类型覆盖只读属性,例如

open class Foo(
    open val x: Number
)

open class Bar(
    override val x: Int
): Foo(x)

This is the case when you do override val value: T .当您override val value: T时就是这种情况。 T is a subtype of Number . TNumber的子类型。

This is because someone accessing an instance of Bar via Foo can still access x without breaking anything.这是因为通过Foo访问Bar实例的人仍然可以访问x而不会破坏任何内容。

val f: Foo = Bar(100)
val number: Number = f.x // 100 is a Number, so everything is fine

However, if x can be set, this would break:但是,如果可以设置x ,这将中断:

val f: Foo = Bar(100)
// Foo.x declares a setter that takes a Number. Double is a Number, so this should be possible
// But what this actually does though, is that it sets Bar.x at runtime, which is an Int!
f.x = 2.5

You probably meant for NumberData<T> to inherit Data<T> instead.您可能希望NumberData<T>改为继承Data<T>


It is also not possible to set the value of a Data<*> .也无法设置Data<*>value This is because you don't know what the exact type of Data it is.这是因为您不知道它的确切类型Data是什么。 Is it a Data<String> ?它是Data<String>吗? Data<Long> ? Data<Long> ? Or Data<SomethingElse> ?还是Data<SomethingElse> If you don't know that, how would you know that value can take the value 5 ?如果你不知道,你怎么知道value可以取值5

To do what you're trying to do, you don't need to set the value at all.要做你想做的事,你根本不需要设置value You can just do:你可以这样做:

val map = dataSet.associate { it.name to it.value }
val result = translate
    .split(" ")
    .mapNotNull { map[it]?.toString() }
    .joinToString(" ")
println(result)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM