如何在 Kotlin 中迭代类的属性

huangapple go评论60阅读模式
英文:

How to iterate over class properties in Kotlin

问题

override fun toJson(): JsonObject {
    val map: HashMap<String, JsonObject> = HashMap()
    for (child in this::class.declaredMemberProperties) {
        map[child.name] = (child.get(this) as ConfigElement).toJson()
    }
    return JsonObject(map)
}
英文:

I have an abstract class ConfigGroup that has the super class ConfigElement.
In ConfigElement a method toJson() is definded. ConfigGroup overrides this method.

Currently toJson() in ConfigGroup looks like this:

override fun toJson(): JsonObject {
        val map: HashMap&lt;String, JsonObject&gt; = HashMap()
        for (child in this::class.declaredMemberProperties) {
            map[child.name] = (child.get(this) as ConfigElement).toJson()
        }
        return JsonObject(map)
    }

But when I try to compile it I get Type mismatch: inferred type is ConfigGroup but Nothing was expected

I am a bit confused since it is almost the same as the example in Kotlin: Iterate over components of object . The only difference being that I try to get the properties from within the same class and therefore have to use the this keyword.

答案1

得分: 1

问题在于Kotlin编译器不足够智能,无法理解你在::classget()中使用完全相同的对象。在这个特定情况下,这是类型安全的,可以允许。然而,在更通用的情况下,从一个对象中获取KProperty并使用它来获取值是不类型安全的,Kotlin故意不允许这样做。

我们可以通过进行未经检查的转换来以与编译器不理解代码类型安全的其他情况相同的方式来解决问题:

(child as KProperty1<ConfigGroup, *>).get(this) // 或者甚至:KProperty1<Any, *>

但等等,为什么获取一个变量类型为Animal的成员,然后对animal实例使用它不是类型安全的呢?难道不能保证animal具有所有动物所需的成员吗?嗯,并不完全是这样的。我们可能会获取狗的成员,然后对猫使用它,因为它们都是动物。

fun main() {
    printProperties(Dog(), Cat())
}

fun printProperties(animal1: Animal, animal2: Animal) {
    for (prop in animal1::class.declaredMemberProperties) {
        println("${prop.name}: ${(prop).get(animal2)}") // 不是类型安全的,所以不能编译
    }
}

printProperties内部,animal1animal2都是Animal类型,但它们的运行时类型不同,因此从animal1获取属性并在animal2上使用它们不是类型安全的。

Kotlin通过使用协变性来防止这种问题。如果我们使用Animal::class,我们会得到一个KClass<Animal>,因此我们可以使用这个类来消费动物对象。但如果我们使用animal::class,我们会得到KClass<out Animal>,因为我们无法确定animal的确切类型。KClass<out Animal>基本上表示我们有一个动物或其子类型的KClass,我们不知道是哪一个。默认情况下,我们不能使用这个KClass来消费动物对象。

在你的特定情况下,可以确保我们从完全相同的运行时类型中获取了KClass,然后尝试消费它,因此这是类型安全的,我们可以安全地使用未经检查的转换。

英文:

The problem here is that the Kotlin compiler isn't smart enough to understand you use exactly the same object for ::class and for get(). In this specific case this is type-safe and could be allowed. However, in more generic case acquiring a KProperty from an object and using it to fetch values isn't type-safe and Kotlin intentionally disallows it.

We can fix the problem in the same way as in other cases where the compiler doesn't understand the code is type-safe - by making an unchecked cast:

(child as KProperty1&lt;ConfigGroup, *&gt;).get(this) // or even: KProperty1&lt;Any, *&gt;

But wait, why acquiring members of a variable typed as Animal and then using it against animal instance isn't type-safe? Isn't it guaranteed animal has all required members for an animal? Well, not exactly. We may acquire members of a dog and then use it against a cat, because both of them are animals.

fun main() {
    printProperties(Dog(), Cat())
}

fun printProperties(animal1: Animal, animal2: Animal) {
    for (prop in animal1::class.declaredMemberProperties) {
        println(&quot;${prop.name}: ${(prop).get(animal2)}&quot;) // isn&#39;t typesafe, so doesn&#39;t compile
    }
}

Inside printProperties, both animal1 and animal2 are of type Animal, but their runtime types are different and because of that it is not type-safe to acquire properties from animal1 and use them against animal2.

Kotlin protects against this type of a problem by using variance. If we do Animal::class we get a KClass&lt;Animal&gt;, so we can use this class for consuming animal objects. But if we do animal::class, we get KClass&lt;out Animal&gt;, because we can't be sure what is the exact type of animal. KClass&lt;out Animal&gt; basically says we have a KClass for an animal or one of its subtypes and we don't know which one. By default we can't use this KClass for consuming animal objects.

In your specific case it is guaranteed we acquired KClass from exactly the same runtime type we then try to consume, so this is type-safe and we can safely use an unchecked cast.

huangapple
  • 本文由 发表于 2023年6月19日 01:24:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76501757.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定