【问题标题】:Kotlin check if elements in list with erased type are instances of a Java classKotlin 检查列表中具有擦除类型的元素是否是 Java 类的实例
【发布时间】:2021-05-19 11:19:34
【问题描述】:

我有一个存储resultClass 的类,我想做一些类型检查并确保用户在调用getTypeSafeResults 函数时获得类型安全列表。

class MyClass<T>(
    private val resultClass: Class<T>
){
    fun getTypeSafeResults() : List<T> {
        val results: List<Any?> = remoteFetchResults()
        return results.filterIsInstance(resultClass)
    }
}

这个 API 必须是 Java 友好的,因此 resultClass 是一个 Java 类。

但是,当 resultClass=Integer::class.java / resultClass=Long::class.java 时,结果包含这些的 Kotlin 版本,例如kotlin.Int / kotlin.Long,它们与filterIsInstance(resultClass) 不匹配。

有没有办法手动检查 resultClass 是否是 Java 版本的 Kotlin 类型?我已经按照下面这个 sn-p 的方式进行了尝试,但没有运气:

(当 it 是 kotlin Long 时,it.javaClass.kotlin="class kotlin.Long"resultClass="long"

fun getTypeSafeResults(results: List<Any?>) : List<T> {
    return results.map {
        uncheckedCast(
            if (resultClass.isAssignableFrom(it.javaClass)) { // doesn't match kotlin long to java Long
                it 
            } else if (it.javaClass.kotlin == resultClass) { // doesn't match kotlin long to java Long
                it
            } else {
                throw Exception("Attempted to cast result of type ${it.javaClass} to $resultClass")
            }
        )
    }
}

【问题讨论】:

  • 它可以与results.filter { it.javaClass == resultClass } as List&lt;T&gt;一起使用吗?
  • 不行,resultClass 是"long",it.javaClass 是"class java.lang.Long"resultClass == it::class.javaObjectTyperesultClass == it::class 也没有

标签: java kotlin generics


【解决方案1】:

我做了一些实验来更好地了解发生了什么。

所有这些都正确找到 42:

val list = listOf(42, 32L, "bob")
println(list.filter { it::class == Int::class })
println(list.filter { it::class == java.lang.Integer::class.java.kotlin })
println(list.filter { it::class == Int::class.java.kotlin })
println(list.filter { it.javaClass == Int::class.javaObjectType })
println(list.filter { it.javaClass == java.lang.Integer::class.java })

只有这个不会:

println(list.filter { it.javaClass == Int::class.java })

因此,您可以做的是确保您在用户给您的课程中使用javaObjectType

class MyClass<T : Any>(
    resultClass: Class<T>
){
    private val effectiveClass = resultClass.kotlin.javaObjectType

    fun getTypeSafeResults() : List<T> {
        val results: List<Any?> = remoteFetchResults()
        return results.filterIsInstance(effectiveClass)
    }
}

【讨论】:

  • 次要:::class.java.kotlin::class相同
  • @Lino 理论上是的,但是由于我使用的是equals(),所以我想检查一下来回不会弄乱平等。这很重要,因为该库的用户将传递Class&lt;T&gt;,因此OP 的类将无法直接访问::class 版本。无论如何我不得不承认,如果有任何区别,它很可能只有在使用不同的类加载器时才可见
  • 当我尝试resultClass.kotlin.javaObjectType 我得到None of the following candidates is applicable because of receiver type mismatch: public val &lt;T : Any&gt; Class&lt;TypeVariable(T)&gt;.kotlin: KClass&lt;TypeVariable(T)&gt; defined in kotlin.jvm
  • @bonapart3 啊,你需要在MyClass的声明中添加一个绑定在T上的类型,比如T : Any。因为我猜.kotlin 仅适用于不可为空的类型(否则它们本身不是类)。我编辑了我的答案。
  • @bonapart3 我不确定如何在这里允许T?。您如何期望用户提供 Java Class&lt;T&gt;,其中 T 可以为空?我不相信这是可能的。所以实际上T 本身永远不能为空,你应该把: Any 绑定。如果您想在输出列表中允许 null items,那就另当别论了,您只需将返回类型更改为 List&lt;T?&gt;。但是,在这种情况下,您的用户将不得不一直处理可为空的元素。无论如何,您评论中的代码是关于返回类型 List&lt;T&gt; 的谎言,并且在 null 情况下不安全的强制转换是错误的。
猜你喜欢
  • 2020-01-02
  • 1970-01-01
  • 2014-10-31
  • 2016-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-06
  • 2012-10-26
相关资源
最近更新 更多