1

我期待看到输出

black
white

用下面的代码

package delegate

import kotlinx.coroutines.runBlocking
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

open class Color(private val name: String) {
    override fun toString(): String {
        return name
    }
}

class Black : Color("black")
class White : Color("white")

class ColorCollection {
    private val black = Black()
    private val white = White()
    val list = listOf(black, white)
}

class Palette {
    val black: Black by ColorDelegate()
    val white: White by ColorDelegate()
    val colorCollection = ColorCollection()
}

class ColorDelegate<T> : ReadOnlyProperty<Palette, T> {
    override fun getValue(thisRef: Palette, property: KProperty<*>): T {
        return thisRef.colorCollection.list.mapNotNull { it as? T }.first()
    }
}

fun main() = runBlocking {
    val palette = Palette()
    println(palette.black)
    println(palette.white)
}

但是,我只得到黑色输出,然后Exception in thread "main" java.lang.ClassCastException: delegate.Black cannot be cast to delegate.White. 我发现通过这一行thisRef.colorCollection.list.mapNotNull { it as? T },我期望它只返回列表中可以安全地转换为泛型类型的值,否则返回 null。例如,当访问 Palette 中的黑色委托属性时,我应该只看到 1 返回的黑色元素thisRef.colorCollection.list.mapNotNull { it as? T },它实际上返回了两个(黑色和白色)。it as? T不管 T 是什么,总能以某种方式工作。我还尝试在该行放置一个断点,尝试将“abcdef”设置为 T?,它也可以工作,我希望看到 String 无法转换为 Black 的转换异常...... 在此处输入图像描述在此处输入图像描述

这是一个错误...?

4

2 回答 2

1

请记住,类型擦除是 Kotlin 中的一个东西,因此运行时不知道Tin是什么it as? T,因此无法为您检查强制转换。因此,演员表总是成功的(以后会有其他事情失败)。另见这篇文章。IntelliJ 应该在这里给你一个“未经检查的演员表”警告。

T因此,您可以使用参数检查类型,而不是使用检查类型property

class ColorDelegate<T> {
    operator fun getValue(thisRef: Palette, property: KProperty<*>) =
        // assuming such an item always exists
        thisRef.colorCollection.list.first { 
            property.returnType.classifier == it::class 
        } as T
}

fun main()  {
    val palette = Palette()
    println(palette.black) // prints "black"
    println(palette.white) // prints "white"
}

在这里,我检查returnType了属性的类(即您放置委托的属性)是否等于列表元素的运行时类。例如,您也可以更加宽容并检查isSubclassOf

请注意,如果属性的类型是另一个类型参数而不是类,则不会在列表中找到任何元素,例如

class Palette<T> {
    ...
    val foo: T by ColorDelegate()
    ...
}

但是,唉,这对你来说是类型擦除:(

于 2021-10-23T14:10:34.607 回答
0

这是由于使用泛型进行类型擦除。转换为泛型类型总是成功的,因为泛型类型在运行时是未知的。如果将值分配给具体不匹配类型的变量或在其上调用它没有的函数,这可能会导致其他地方出现运行时异常。

由于转换为泛型类型是危险的(它默默地工作,允许在代码中的不同位置发生错误),当您像在代码中那样执行此操作时会出现编译器警告。警告说“未经检查的演员表”,因为演员表在没有类型检查的情况下发生。当您转换为具体类型时,运行时会检查转换站点的类型,如果存在不匹配,它会立即抛出 ClassCastException,或者在安全转换的情况下解析为 null as?

Kotlin 文档中有关类型擦除的信息

于 2021-10-23T14:07:46.783 回答