【问题标题】:Casting generic type on Kotlin在 Kotlin 上转换泛型类型
【发布时间】:2018-04-12 12:40:19
【问题描述】:

如果 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&lt;*&gt; 不能成为解决方案。

【问题讨论】:

  • 我认为问题在于 K::class.java.isEnum 并不意味着 K extends Enum
  • 您想如何使用它?您在这里的逻辑不正确,因为使您的代码正确的唯一解决方案是添加 reified K : Enum?正如你所说。但是正如你所看到的,什么样的对象可以匹配这个条件? ;) 请与我们分享您的用例。
  • @muminers 你会像往常一样使用它:mapOf&lt;String, Int&gt;()mapOf&lt;MyEnum, String&gt;()。后者将实例化一个优化的EnumMap

标签: java generics casting kotlin


【解决方案1】:

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

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),所有转换都消失了。

【讨论】:

  • 你好!有趣的解决方案:)
【解决方案2】:

没有 clean 方法可以使这项工作 (afaik),因为使 K 符合绑定 K extends Enum&lt;K&gt;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 不是最好的,所以可能还有改进的空间。不过,这是一种可能的解决方法。

【讨论】:

  • 我认为我的方式更干净:)
猜你喜欢
  • 2012-01-02
  • 2012-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多