Call reified function from non-reified one - kotlin

I've got a third-party service that exposes a reified extension function. I would like to wrap this service into an interface-implementation pair for easier testing and encapsulation reason.
The problem is that I can't use this function in my app, because compiler tells me that:
Cannot use 'T' as reified type parameter. Use a class instead.
The structure is like following:
interface ThirdPartyService
inline fun <reified T> ThirdPartyService.execute(): T
interface Wrapper {
fun <T> execute(): T
}
class WrapperImpl(private val thirdPartyService: ThirdPartyService) : Wrapper {
override fun <T> execute(): T =
thirdPartyService.execute()
}
In this case calling thirdPartyService.execute() causes a compiler issue I mentioned above.
Is there any way to overcome this issue? What does this Use a class instead. actually means?

When you mark a type as reified, you're allowing it to be used explicitly in the function in places that it would normally be erased.
However the type T in your Wrapper.execute function here is not reified which means that it will be erased at runtime and therefore unable to be 'passed' to the ThirdPartyService.execute function.
You can read more about reified types here.
In essence, the whole point of a function with a reified type is that it can use the type at runtime. So calling it without a concrete type doesn't make any sense and due to type erasure, a non-reified T is not a concrete type.

Related

Kotlin: Generic types in Kotlin

To get the class definition to be used for example for json deserialization the following can be used in Kotlin:
Map::class.java
A example usage is the following:
val map = mapper.readValue(json, Map::class.java)
But now how to have the generic type definition?
Something like this does not compile:
val map = mapper.readValue(decodedString, Map<String, String>::class.java)
So my question is: What is the generic equivalent to *::class.java
Class<T> (in Java) or KClass<T> (in Kotlin) can only represent classes, not all types. If the API you're using only uses Class<T> or KClass<T>, it simply doesn't support generic types (at least in those functions).
Instead, KType (or Type in Java) is the proper type to use to represent the complete type information including generics. You could use it this way:
val myMapType: KType = typeOf<Map<String,String>>()
Unfortunately, KType doesn't have a type parameter (it's not KType<T>), and that makes it impossible to use for compile-time type checking: you can't have the equivalent of fun deserialize(Input, KClass<T>): T using KType instead of KClass, because you can't define the T for the return type by using only a KType argument.
There are several tricks to work around this:
In both Java and Kotlin, one of the ways is to get this information through inheritance by providing a generic superclass and inheriting from it.
In general, serialization APIs (especially the deserializing part) provide workarounds using this, such as Jackson's TypeReference or Gson's TypeToken. It's basically their version of Type but with a type parameter to have some compile-time type safety.
In Kotlin, there is sometimes another way depending on the situation: making use of reified type parameters. Using inline functions, the compiler can know more information at compile time about the type parameters by replacing them with the actual inferred type at the call site when inlining the function's body. This allows things like T::class in the inline function's body. This is how you can get functions like typeOf to get a KType.
Some Kotlin-specific APIs of deserialization libraries use inline functions to remove the hassle from the user, and get type information directly. This is what jackson-module-kotlin does by providing an inline readValue extension without a Class argument, which reifies the type parameter to get the target type information

Reflection and Generics in Kotlin

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.
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.
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?
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.

Trying to use Kotlin reified type parameters with a library class

I am using JGraphT library from Kotlin and I want to have my graph parametrised. However, the way I was trying to do it does not work, since U is not defined at the compile time and can not be used for reflection. So I get the error message "Cannot use T as a reified type parameter. Use a class instead." As far as I know, reified type parameters can be used for inline functions to resolve this, but I can not see how it could help me here, especially knowing that I can not change the library code.
Any ideas would be appreciated.
class GraphManipulation<T,U> {
val g = DefaultDirectedGraph<T, U>(U::class.java)
...}
The problem is not with the use of DefaultDirectedGraph, but that U is not reified in your GraphManipulation class. Since classes can't have reified class parameters (yet?) you need to take the class as a constructor parameter:
class GraphManipulation<T,U>(private val uClass: Class<U>) {
val g = DefaultDirectedGraph<T, U>(uClass)
}
Where reified can help is to make a helper method
inline fun <T, reified U> GraphManipulation(): GraphManipulation<T,U> = GraphManipulation(U::class.java)

generics compilation error in kotlin [duplicate]

I have a generically typed class Builder<T> that takes a constructor argument Class<T> so I can keep the type around. This is a class that I use a lot in java code so I don't want to change the signature.
When I try to use the constructor like this:
Builder<List<Number>>(List<Number>::class)
I get an error: "Only classes are allowed on the left hand side of a class literal"
Any way to resolve this?
I can't change the constructor for Builder, too many java classes rely upon it.
I understand the whole type erasure issue, I really just want to make the compiler happy.
Due to generic type erasure List class has a single implementation for all its generic instantiations. You can only get a class corresponding to List<*> type, and thus create only Builder<List<*>>.
That builder instance is suitable for building a list of something. And again due to type erasure what that something is you can decide by yourself with the help of unchecked casts:
Builder(List::class.java) as Builder<List<Number>>
Builder(List::class.java as Class<List<Number>>)
Another approach is to create inline reified helper function:
inline fun <reified T : Any> Builder() = Builder(T::class.java)
and use it the following way:
Builder<List<Number>>()
The solution is to use reified generics in couple with super class tokens.
Please refer to this question for the method explained. Constructors in Kotlin don't support reified generics, but you can use TypeReference described there to write a builder factory function which will retain actual generic parameters at runtime:
inline <reified T: Any> fun builder(): Builder<T> {
val type = object : TypeReference<T>() {}.type
return Builder(type)
}
Then inside Builder you can check if type is ParameterizedType, and if it is, type.actualTypeArguments will contain the actual generic parameters.
For example, builder<List<Number>>() will retain the information about Number at runtime.
The limitation of this approach is that you cannot use non-reified generic as a reified type parameter because the type must be known at compile-time.

Kotlin's reflection : Unknown type parameter

I am running some experiments on Kotlin's reflection.
I am trying to get a reflection object of a generic class with its argument.
In Java, that would be a ParameterizedType.
The way to get such a thing using Java's reflection API is a bit convoluted: create an anonymous subclass of a generic class, then get its super-type first parameter.
Here's an example:
#Suppress("unused") #PublishedApi
internal abstract class TypeReference<T> {}
inline fun <reified T> jGeneric() =
((object : TypeReference<T>() {}).javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
When I println(jGeneric<List<String?>>()), it prints java.util.List<? extends java.lang.String>, which is logical considering that Kotlin's List uses declaration-site out variance and that Java types have no notion of nullability.
Now, I would like to achieve the same kind of result, but with the Kotlin reflection API (that would, of course, contain nullability information).
Of course, List<String>::class cannot work since it yields a KClass. and I am looking for a KType.
However, when I try this:
inline fun <reified T> kGeneric() =
(object : TypeReference<T>() {})::class.supertypes[0].arguments[0].type
When I println(kGeneric<List<String?>>()), it prints [ERROR : Unknown type parameter 0], which is quite... well, anticlimactic ;)
How can I get, in Kotlin, a KType reflecting List<String> ?
To create a KType instance in Kotlin 1.1, you have two options:
To create a simple non-nullable type out of a KClass, where the class is either not generic or you can substitute all its type parameters with star projections (*), use the starProjectedType property. For example, the following creates a KType representing a non-nullable type String:
val nonNullStringType = String::class.starProjectedType
Or, the following creates a KType representing a non-nullable type List<*>:
val nonNullListOfSmth = List::class.starProjectedType
For more complex cases, use the createType function. It takes the class, type arguments and whether or not the type should be nullable. Type arguments are a list of KTypeProjection which is simply a type + variance (in/out/none). For example, the following code creates a KType instance representing List<String>:
val nonNullStringType = String::class.starProjectedType
val projection = KTypeProjection.invariant(nonNullStringType)
val listOfStrings = listClass.createType(listOf(projection))
Or, the following creates the type List<String>?:
val listOfStrings = listClass.createType(listOf(projection), nullable = true)
Both starProjectedType and createType are defined in package kotlin.reflect.full.
We're planning to introduce the possibility of getting a KType instance simply from a reified type parameter of an inline function which would help in some cases where the needed type is known statically, however currently it's not entirely clear if that's possible without major overhead. So, until that's implemented, please use the declarations explained above.