I've written myself into a corner where I want an instance of Class<Foo<Bar>>
. While there's no apparent reason that this shouldn't be valid, there seems to be no way to create one. Foo<Bar>::class.java
is a syntax error, and Kotlin does not provide a public constructor for Class
.
The code I'm writing is an abstraction layer over gson. Below is an overly-simplified example:
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"
In my production code, there are already dozens of subclasses that inherit from the base class, and many that override the "parse" function Ideally I'd like a solution that doesn't require refactoring the existing subclasses.
CodePudding user response:
Actually, there is a reason this is impossible. Class
(or Kotlin's KClass
) can't hold parameterized types. They can hold e.g. List
, but they can't List<String>
. To store Foo<Bar>
you need Type (or Kotlin's KType
) and specifically ParameterizedType. These classes are somewhat more complicated to use and harder to acquire than simple Class
.
The easiest way to acquire Type
in Kotlin is by using its typeOf() utility:
typeOf<Foo<Bar>>().javaType
Gson supports both Class
and Type
, so you should be able to use it instead.
CodePudding user response:
The closest you'll get is Boxed::class.java
. This is not a language restriction but a JVM restriction. JVM has type erasure, so no generic types exist after compilation (thats also one of the reasons generics cant be primitives, as they need to be reference types to behave).
Does it work with the raw Boxed type class?
CodePudding user response:
For this case, it looks like
BaseParser<Boxed<Bar>>(Boxed::class.java as Class<Boxed<Bar>>)
could work (that is, it will both type-check and succeed at runtime). But it depends on what exactly happens in the "Presume that clazz is required for other omitted functions" part. And obviously it doesn't allow actually distinguishing Boxed<Foo>
and Boxed<Bar>
classes.
I'd also consider broot's approach if possible, maybe by making BaseParser
and new
class TypeBaseParser<U : Any>(private val tpe: Type)
extend a common abstract class/interface.