Kotlin 中的反射和泛型

Reflection and Generics in Kotlin

我已经把自己写进了一个角落,我想要一个 Class<Foo<Bar>> 的实例。虽然没有明显的理由表明这不应该是有效的,但似乎没有办法创建一个。 Foo<Bar>::class.java是语法错误,Kotlin没有为Class.

提供public构造函数

我正在编写的代码是 gson 之上的抽象层。下面是一个过于简化的例子:

class Boxed<T : Any> (val value: T)

class BaseParser<U : Any> (
    private val clazz: Class<U>
) {
    //This works for 98% of cases
    open fun parse(s: String): U {
        return gson.fromJson(s, clazz)
    }
    
    //Presume that clazz is required for other omitted functions
}

//Typical subclass:
class FooParser : BaseParser<Foo>(Foo::class.java)

// Edge Case 
class BarParser : BaseParser<Boxed<Bar>>(Boxed<Bar>::class.java) {
    override fun parse(s: String): Boxed<Bar> {
        return Boxed(gson.fromJson(s, Bar::class.java))
    }
}
// not valid: "Only classes are allowed on the left hand side of a class literal"

在我的生产代码中,已经有几十个从基 class 继承的子class,并且许多重写了“解析”功能理想情况下,我想要一个解决方案不需要重构现有的 subclasses.

事实上,这是不可能的。 Class(或 Kotlin 的 KClass)不能保存参数化类型。他们可以容纳例如List,但他们不能 List<String>。要存储 Foo<Bar>,您需要 Type (or Kotlin's KType) and specifically ParameterizedType。这些 类 比简单的 Class.

使用起来更复杂,也更难获得。

在 Kotlin 中获取 Type 的最简单方法是使用其 typeOf() 实用程序:

typeOf<Foo<Bar>>().javaType

Gson 同时支持 ClassType,因此您应该可以使用它。

最接近的是 Boxed::class.java。这不是语言限制,而是 JVM 限制。 JVM 具有类型擦除功能,因此编译后不存在泛型(这也是泛型不能是基元的原因之一,因为它们需要引用类型才能表现)。 它是否适用于原始盒装类型 class?

对于这种情况,它看起来像

BaseParser<Boxed<Bar>>(Boxed::class.java as Class<Boxed<Bar>>)

可以工作(也就是说,它会在运行时进行类型检查并成功)。但这取决于“假设其他省略的函数需要 clazz”部分中究竟发生了什么。显然它不允许实际区分 Boxed<Foo>Boxed<Bar> 类.

如果可能的话,我也会考虑 broot 的方法,也许通过制作 BaseParser 和新的

class TypeBaseParser<U : Any>(private val tpe: Type)

扩展一个共同的摘要class/interface。