Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
272 views
in Technique[技术] by (71.8m points)

kotlin - Unexpected reified type inference

I expected the below code to be failed to compile at the last line. because it missed some type expression for type inference.

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())

Outputs and versions in my terminal:

$ 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)

This script raises java.lang.IllegalArgumentException: Unexpected class: class Test$Test at 13th line in getByClass. The get() seems to get Test class as a reified class.

How I should write get() as type safe? I want the code to be failed at compile, not at runtime.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is an alternative way of getting a type from a sealed class. You can use the sealedSubclasses from KClass:

sealed class Service {
  
  object A : Service()
  object B : Service()
  object C : Service()

  companion object {
    val all: List<Service> = Service::class.sealedSubclasses
            .map { it.objectInstance as Service }

    fun findByClass(kClass: KClass<out Service>): Service = all.first { it::class == kClass }
}

You could then look for a concrete sub type using findByClass():

val serviceC: ServiceC = Service.findByClass(Service.C::class)

Note findByClass() can safely return a non null value, though first can throw an exception when the element is not present, but because of parameter kClass: KClass<out Service> we can only pass a sub type of Service. So that class type must be available.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...