英文:
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<String, JsonObject> = 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编译器不足够智能,无法理解你在::class
和get()
中使用完全相同的对象。在这个特定情况下,这是类型安全的,可以允许。然而,在更通用的情况下,从一个对象中获取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
内部,animal1
和animal2
都是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<ConfigGroup, *>).get(this) // or even: KProperty1<Any, *>
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("${prop.name}: ${(prop).get(animal2)}") // isn't typesafe, so doesn'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<Animal>
, so we can use this class for consuming animal objects. But if we do animal::class
, we get KClass<out Animal>
, because we can't be sure what is the exact type of animal
. KClass<out Animal>
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论