Kotlin 意外具体化类型推断

Kotlin 意外具体化类型推断,kotlin,Kotlin,我期望下面的代码在最后一行编译失败。因为它错过了一些用于类型推断的类型表达式 test.kts: import kotlin.reflect.KClass sealed class Test { object A : Test() object B : Test() companion object { fun <T : Test> getByClass(c: KClass<T>): T = @Suppr

我期望下面的代码在最后一行编译失败。因为它错过了一些用于类型推断的类型表达式

test.kts

import kotlin.reflect.KClass

sealed class Test {
    object A : Test()
    object B : Test()

    companion object {
        fun <T : Test> getByClass(c: KClass<T>): T =
            @Suppress("UNCHECKED_CAST")
            when (c) {
                A::class -> A as T
                B::class -> B as T
                else -> throw IllegalArgumentException("Unexpected class: $c")
            }

        inline fun <reified T : Test> get(): T = getByClass(T::class)
    }
}

fun f1(a: Test.A) {
    println(a::class)
}

fun f2(a: Any) {
    println(a::class)
}

// Works right
f1(Test.get())

// Just invocation get raises compile error
// Test.get()

// Providing type raises compile error
// Test.get<Any>()
// f2(Test.get<Any>())

// This does not throw compile error
f2(Test.get())
import kotlin.reflect.KClass
密封类试验{
对象A:Test()
对象B:测试()
伴星{
趣味getByClass(c:K类):T=
@抑制(“未选中的_CAST”)
何时(c){
A::class->A作为T
B::class->B作为T
else->抛出IllegalArgumentException(“意外类:$c”)
}
内联fun get():T=getByClass(T::class)
}
}
趣味f1(a:测试a){
println(a::类)
}
乐趣f2(a:任何){
println(a::类)
}
//正常工作
f1(Test.get())
//仅调用get会引发编译错误
//Test.get()
//提供类型会引发编译错误
//Test.get()
//f2(Test.get())
//这不会引发编译错误
f2(Test.get())
我的终端中的输出和版本:

$ kotlinc -version
info: kotlinc-jvm 1.4.21 (JRE 11.0.5+10)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.util.ReflectionUtil to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
WARNING: Please consider reporting this to the maintainers of com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
$ kotlinc -script test.kts
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.util.ReflectionUtil to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
WARNING: Please consider reporting this to the maintainers of com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
class Test$Test$A
java.lang.IllegalArgumentException: Unexpected class: class Test$Test
        at Test$Test$Companion.getByClass(test.kts:13)
        at Test.<init>(test.kts:32)
$kotlinc-版本
信息:kotlinc jvm 1.4.21(JRE 11.0.5+10)
警告:发生了非法的反射访问操作
警告:com.intellij.util.ReflectionUtil对方法java.util.ResourceBundle.setParent(java.util.ResourceBundle)的非法反射访问
警告:请考虑将此报告给COM.ITELLJ.U.I.CurrutuUTL的维护者。
警告:使用--invalize access=warn以启用对进一步非法访问操作的警告
警告:所有非法访问操作将在未来版本中被拒绝
$kotlinc-script test.kts
警告:发生了非法的反射访问操作
警告:com.intellij.util.ReflectionUtil对方法java.util.ResourceBundle.setParent(java.util.ResourceBundle)的非法反射访问
警告:请考虑将此报告给COM.ITELLJ.U.I.CurrutuUTL的维护者。
警告:使用--invalize access=warn以启用对进一步非法访问操作的警告
警告:所有非法访问操作将在未来版本中被拒绝
类测试$Test$A
java.lang.IllegalArgumentException:意外类:类测试$Test
测试$Test$Companion.getByClass时(Test.kts:13)
试验时。(试验kts:32)
此脚本在
getByClass
的第13行引发
java.lang.IllegalArgumentException:Unexpected类:类测试$Test
get()
似乎将
Test
类作为一个具体化的类


如何将
get()
写入类型安全?我希望代码在编译时失败,而不是在运行时失败。

原因是您在类的静态块测试中执行此操作,因此它将传递函数主类类型,而不是实例类型(它是类方法而不是实例方法)。您需要对其进行更改,以便只能在A和B(非静态)等实例上调用它。像这样的方法可以奏效:

sealed class Test {
    object A : Test()
    object B : Test()

    companion object {
        fun <T : Test> getByClass(c: KClass<T>): T =
            @Suppress("UNCHECKED_CAST")
            when (c) {
                A::class -> A as T
                B::class -> B as T
                else -> throw IllegalArgumentException("Unexpected class: $c")
            }
    }

    fun get(): Test {
        return getByClass(this::class)
    }
}


fun main() {
    val b = Test.B
    println(b.get())
}

密封类测试{
对象A:Test()
对象B:测试()
伴星{
趣味getByClass(c:K类):T=
@抑制(“未选中的_CAST”)
何时(c){
A::class->A作为T
B::class->B作为T
else->抛出IllegalArgumentException(“意外类:$c”)
}
}
fun get():测试{
返回getByClass(this::class)
}
}
主要内容(){
val b=测试b
println(b.get())
}

这是从
密封类
获取类型的另一种方法。您可以使用
KClass
中的
sealedsubclass

密封类服务{
对象A:服务()
对象B:服务()
对象C:服务()
伴星{
val all:List=Service::class.sealedSubclass
.map{it.objectInstance as Service}
fun findByClass(kClass:kClass):Service=all.first{it::class==kClass}
}
然后可以使用
findByClass()
查找具体的子类型:

val serviceC:serviceC=Service.findByClass(Service.C::class)
注意
findByClass()
可以安全地返回非空值,尽管
first
可以在元素不存在时引发异常,但由于参数
kClass:kClass
我们只能传递
服务的子类型
。因此类类型必须可用。

有趣的问题。我可以问一下这个场景吗?您想知道什么ieve?也许有不同的方法。或者你只是想解释一下如何使它在编译时失败?嗯..实际上我想要两者都有。经过一些考虑后选择了尾随问题。我想知道三件事。1)有没有更好的实现这个密封类,它通过具体化的clas解析实例s?2)我应该如何将
get()
编写为类型安全?(尾随的一个)因为运行时错误调查比编译错误更困难。3)此推断是否遵循规范?我添加了一个示例,效果良好。我只需要使用具象泛型的
getByClass
包装静态方法。
f1
类型推断成功,因为
f1
参数是
Test.A
。我想知道如何编写
get())
要在调用
f2
行中获取编译错误,我知道您正在尝试执行的操作,但静态方法不是执行此操作的方法。如果您想知道子类的类型,您需要在实例上调用方法,否则您将始终获得超类的类型。您尝试执行的操作无法工作,因为您需要的是o知道它的类型F1是成功的,因为您强制它成功。您所做的就像这样调用get方法
Test.get()
。我再说一遍,除非您有实例,否则无法获取子类类型。没有实例,您怎么可能知道类型?我知道“除非有实例,否则无法获取子类类型。”因此,我希望在这种情况下出现编译错误。谢谢。我没有