在Kotlin

时间:2018-04-12 12:40:20

标签: java generics casting kotlin

如果K为Enum,我试图让mapOf()返回EnumMap。但是,错误发生在Enum(K)的第一个类型变量

代码:

@kotlin.internal.InlineOnly
public inline fun <reified K, V> mapOf(): Map<K, V> =
    if (K::class.java.isEnum) EnumMap<K, V>(K::class.java)
    else emptyMap()

错误:

Type argument is not within its bounds.
Expected: Enum<K!>!
Found: K

我也想提供Enum以外的其他对象,因此reified K: Enum<*>无法解决问题。

2 个答案:

答案 0 :(得分:3)

使用类型擦除的邪恶作弊:

import java.util.concurrent.TimeUnit // you can use any other enum as well

@Suppress("UNCHECKED_CAST")
public inline fun <reified K, V> mapOf(): Map<K, V> =
    if (K::class.java.isEnum) 
        EnumMap<TimeUnit, V>(K::class.java as Class<TimeUnit>) as Map<K, V>
    else emptyMap()

在运行时,这实际上只是EnumMap(K::class.java),所有演员都会消失。

答案 1 :(得分:2)

没有 clean 方法来完成这项工作(afaik),因为使K符合绑定K extends Enum<K>EnumMap所需)将需要动态添加绑定到K,这是不可能的,因为泛型是编译时机制。

在java中,您可以将EnumMap构造为原始类型,并将其取消选中转换为目标类型(即断言它是正确的)。但是Kotlin不允许你这样做,因为它不允许原始类型。我可以想出一种方法来解决这个限制,即反思:

val constructor = EnumMap::class.constructors.find {
    con ->
        con.parameters.size == 1
        && con.parameters[0].type.jvmErasure == Class::class
}!!

inline fun <reified K, V> mapOf(): Map<K, V> =
    if (K::class.java.isEnum) constructor.call(K::class.java) as Map<K, V>
    else emptyMap()

我的Kotlin并不是最好的,所以仍有改进的余地。这是一种可能的解决方法。