Kotlin generic interface resolver - kotlin

I'm trying to build a resolver that given some domain context return back an implementation of a generic interface. The code is the following (domain abstracted):
interface Interface<T>
class StringImplementation: Interface<String>
class BooleanImplementation: Interface<Boolean>
class Resolver {
fun <T : Any> resolve(implementation: String): Interface<T> {
return when (implementation) {
"string" -> StringImplementation()
"boolean" -> BooleanImplementation()
else -> throw IllegalArgumentException()
}
}
}
This snippet looks good to me but the compiler is complaining because Type missmatch: Required: Interface<T> Found: StringImplementation at line 11 and Type missmatch: Required: Interface<T> Found: BooleanImplementation at line 12.
Why is that a problem? I though setting <T : Any> in the method contract would allow to return an implementation of any type. The constraing here is that the return type of the method resolve must be Interface<T>, replacing it with Interface<*> would make the compiler shut up but is not what we need.

TL;DR
A function can have exactly 1 return type, but your function has 2 different return types.
Only this would work:
interface Interface<T>
class StringImplementation: Interface<String>
class BooleanImplementation: Interface<Boolean>
class Resolver {
fun resolve(implementation: String): Interface<*> { // <-- star
return when (implementation) {
"string" -> StringImplementation()
"boolean" -> BooleanImplementation()
else -> throw IllegalArgumentException()
}
}
}
Explanation
Looking from the function definition perspective, it has to have an explicit, clear return type. Interface<T> says it should be something extending Interface and the explicit type T which concrete implementation can be known by the start of execution of the function.
There is no way in your code to know what T will be when you call resolve. How else would you imagine the function to know what it will return back?!
Shortened: A function can have exactly 1 return type, but your function has 2 different return types (Interface<String> / Interface<Boolean>).
Continue to read here if you want to dig down into generics and get a more technical description.

The compiler cannot know if T matches the implementation variable. Even if implementation is string, T could be of another type then String. So you either can erase the generic type like #Neo mentioned or you need to cast the return type.
interface Interface<T>
class StringImplementation: Interface<String>
class BooleanImplementation: Interface<Boolean>
class Resolver {
inline fun <reified T : Any> resolve(): Interface<T> {
return when (T::class) {
String::class -> StringImplementation() as Interface<T>
Boolean::class -> BooleanImplementation() as Interface<T>
else -> throw IllegalArgumentException()
}
}
}
To have more type safety, you can use a reified parameter and use this to resolve the type. (Note that the cast is still necessary)

I though setting <T : Any> in the method contract would allow to return an implementation of any type.
No, it means it has to return an implementation of any type the caller asks for. E.g., in Animesh Sahu's example, resolve<Boolean>("string") must return an Interface<Boolean>, but your implementation of resolve would return a StringImplementation. Of course, it could also be resolve<File>("string") etc.
allow to return an implementation of any type
which the called method chooses is exactly Interface<*>.

Related

Parameterized interfaces in Kotlin

I'm building a Kotlin microservice that processes events of various types and publishes them out to external partner systems. I have all of the event classes implementing a common interface called GenericPartnerEvent:
interface GenericPartnerEvent
data class EventTypeOne(
val key: String,
val payload: String
) : GenericPartnerEvent
I then have a series of event handler classes, one per external partner, that are defined like this:
interface PartnerEventHandler<in T : GenericPartnerEvent> {
fun handleEvent(event: T)
}
class PartnerOneEventTypeOneHandler : PartnerEventHandler<EventTypeOne> {
...
}
I want a generic way to fetch the event handler class of the correct type given a partner and event type; something like:
fun getEventHandler(partner: String, eventType: String): PartnerEventHandler<GenericPartnerEvent> {
if (...something...) return PartnerOneEventTypeOneHandler()
}
But what I inevitably get is something like:
Type mismatch.
Required: PartnerEventHandler<GenericPartnerEvent>
Found: PartnerOneEventTypeOneHandler
Is there a way to do this?
No, since your PartnerEventHandler type is contravariant (in), a subtype's type cannot be upcast. A PartnerEventHandler<EventTypeOne> cannot accept any subtype of GenericPartnerEvent as an input, so a PartnerEventHandler<EventTypeOne> is not a subtype of PartnerEventHandler<GenericPartnerEvent>.
Since you retrieve handlers using a String representation of the type, I don't see how generics buy you anything here anyway. The handle function might as well take an input type of Any and reject incorrect types with an exception or by returning null or something.
FWIW, this approach seems to pass muster with the compiler:
inline fun <reified T : GenericPartnerEvent> getHandlerForPartner(partner: String): PartnerEventHandler<T>? {
return when (partner) {
"partner-one" -> when (T::class) {
EventTypeOne::class -> PartnerOneEventTypeOneHandler() as PartnerEventHandler<T>
EventTypeTwo::class -> PartnerOneEventTypeTwoHandler() as PartnerEventHandler<T>
else -> null
}
"partner-two" -> when (T::class) {
EventTypeOne::class -> PartnerTwoEventTypeOneHandler() as PartnerEventHandler<T>
EventTypeTwo::class -> PartnerTwoEventTypeTwoHandler() as PartnerEventHandler<T>
else -> null
}
else -> null
}
}
It does give me a warning: Unchecked cast: PartnerOneEventTypeOneHandler to PartnerEventHandler<T>. So I guess we'll see what happens at runtime.

How to cast generic type after isAssignableFrom check in Kotlin?

See the example:
class MyTypeAdapter<T : Throwable>
(private val gson: Gson, private val skipPast: TypeAdapterFactory) : TypeAdapter<T>() {
// :Throwable is needed to access the stackTrace field
}
private class ThrowableTypeAdapterFactory : TypeAdapterFactory {
override fun <T> create(gson: Gson, typeToken: TypeToken<T>): TypeAdapter<T>? {
if (Throwable::class.java.isAssignableFrom(typeToken.rawType)) {
return MyTypeAdapter<T>(gson, this) // compile error: Type argument is not within its bound
}
return null
}
}
So in Java we have raw use parameterized class but Kotlin doesn't allow it anymore.
I tried to find something from https://kotlinlang.org/docs/reference/generics.html
but couldn't get a clue. Please advice.
You should be able to cheat thanks to type erasure:
return MyTypeAdapter<Throwable>(gson, this) as MyTypeAdapter<T>
It looks wrong, but the class can't actually do anything different depending on T.
Or if Kotlin won't accept this cast directly (can't check at the moment) something like
return (MyTypeAdapter<Throwable>(gson, this) as MyTypeAdapter<*>) as MyTypeAdapter<T>
or even
return (MyTypeAdapter<Throwable>(gson, this) as Any) as MyTypeAdapter<T>
should work.

Cast reified generic type as non-null with Any upper bound

I’m trying to use a reified type parameter to check if the type argument is nullable, returning a different class implementation based on the nullability of the type argument. This works well, except for the non-null subclass requiring its generic type to have a non-null Any upper bound, in order to have a KClass<T> constructor argument.
This code works as expected:
interface Test
class NullableT<T> : Test
class NonNullT<T> : Test
inline fun <reified T> test(): Test {
return if (null is T) {
NullableT<T>()
} else {
NonNullT<T>()
}
}
test<String?>()::class.simpleName // NullableT
test<String>()::class.simpleName // NonNullT
However, this code has a compiler error:
interface Test
class NullableT<T> : Test
class NonNullT<T : Any>(tClass: KClass<T>) : Test
inline fun <reified T> test(): Test {
return if (null is T) {
NullableT<T>()
} else {
NonNullT<T>(T::class) // <-- error with <T>
// Type argument is not within its bounds. Expected: Any Found: T
}
}
Following the check for !(null is T), there needs to be some way to cast T as having a non-null Any upper bound.
It’s possible to make a non-null T optional. This works:
interface Test
class NullableT<T> : Test
class NonNullT<T : Any> : Test
inline fun <reified T : Any> test(nullable: Boolean): Test {
return if (nullable) {
NullableT<T?>()
} else {
NonNullT<T>()
}
}
test<String>(true)::class.simpleName // NullableT
test<String>(false)::class.simpleName // NonNullT
But I need a way to make a nullable T non-null. This isn’t valid:
interface Test
class NullableT<T> : Test
class NonNullT<T : Any> : Test
inline fun <reified T> test(nullable: Boolean): Test {
return if (nullable) {
NullableT<T>()
} else {
NonNullT<T!!>() // Type parameter 'T' is not an expression
}
}
This works:
import kotlin.reflect.*
interface Test
class NullableT<T> : Test
class NonNullT<T : Any>(tClass: KClass<T>) : Test
inline fun <reified T> test(dummy: Nothing? = null): Test {
return NullableT<T>()
}
inline fun <reified T: Any> test(): Test {
return NonNullT<T>(T::class)
}
fun main(){
println(test<Any>().toString())
println(test<Any?>().toString())
println(test<String>().toString())
println(test<String?>().toString())
}
"But why does this work?" I hear you asking. Well simply, what's happening here is that the Kotlin compiler prefers functions that match the number of passed in parameters (in this case 0) over functions that have default parameters that would result in allowing the number of passed in parameters (in other words, if you call a function with 1 argument, the compiler will prefer a function with 1 parameter over a function with 2 parameters where the 2nd parameter has a default value). However, because T has a different upper bound in the 2 functions, if the type that you're trying to invoke test with doesn't follow the upper bound of the preferred function (which in this case is the one that returns NonNullT), the compiler will fall back to calling the more broad test function (i.e. the one that returns NullableT).
This solution is kinda hacky, but sadly I don’t think there’s any other way to implement it.
No, you can't add dynamic type bound to type parameter since it can be checked only at compile-time because of type erasure.
Kotlin Nullability feature doesn't exist at compile-time too.

Kotlin: generic cast function parameter

Taking my first steps in Kotlin, I'm struggling to find the correct signature for a function that receives an instance of a known class along with the desired output class and then looks in a map of converter lambdas whether the conversion can be done.
Here's an example for Long:
private fun <T> castLong(value: Long, clazz: Class<out T>): T {
// map lookup removed for simplicity
return when (clazz) {
String::class.java -> { value.toString() }
else -> { throw IllegalArgumentException("Unsupported Cast") }
}
}
Where T is the class of the desired return value - let's say String. One should be able to call castLong(aLongValue, String::class.java) and receive an instance of String.
But the compiler says:
Type mismatch: inferred type is String but T was expected
This seems like it should be possible as it is quite straightforward so far but even playing around with reified and other constructs didn't yield any better results.
It happens because it can't smart cast String to T, you have to manually cast it.
Furthermore, since you said you are taking your first steps in Kotlin, I leave here two other "advices" not strictly related to your question:
you can get the class of T making it reified
the brackets of a case using when aren't necessary if the case is one line
private inline fun <reified T> castLong(value: Long): T {
// map lookup removed for simplicity
return when (T::class.java) {
String::class.java -> value.toString()
else -> throw IllegalArgumentException("Unsupported Cast")
} as T
}

How to check generic type in Kotlin?

I have class:
class Generic<T : SuperType>() { ... }
And this code is't correct, but cast s to type T:
fun typeCheck(s: SuperType) {
when(s) {
is T -> // Do something
}
}
If use: s as T - this cast will show warning (unsafe cast).
How check that s is T type?
If you need to check if something is of generic type T you need to to have an instance of Class<T> to check against. This is a common technique in Java however in Kotlin we can make use of an inlined factory method that gets us the class object.
class Generic<T : Any>(val klass: Class<T>) {
companion object {
inline operator fun <reified T : Any>invoke() = Generic(T::class.java)
}
fun checkType(t: Any) {
when {
klass.isAssignableFrom(t.javaClass) -> println("Correct type")
else -> println("Wrong type")
}
}
}
fun main(vararg args: String) {
Generic<String>().checkType("foo")
Generic<String>().checkType(1)
}
Generic types are not reified on the JVM at runtime, so there's no way to do this in Kotlin. The warning is correct because the compiler can't possibly generate any instruction that will fail when the cast is done, so the cast is unchecked, meaning that the program may or may not break at some point later instead.
A related feature which might be of use is reified type parameters in inline functions. Classes can't have reified type parameters though, so if you elaborate a bit more on your use case, I can try helping you achieve what you seem to need.
I know that I'm kinda late to this thread, but I just want to recap on the answer provided by Alexander Udalov.
It is, indeed, impossible to determine the type of a generic parameter in Kotlin unless you're using inline functions and declaring the generic type as reified.
Not sure if I'll be able to answer this question entirely and accurately, but I feel like my contribution might still be valuable for someone who is attempting to do just that. So let's say you have a few data classes, and you want to check which type you're dealing with.
You could use a function like that:
inline fun <reified T> checkType() = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
however, functions that call it must also be inline, so you might have to write something like
inline fun <reified T> someOtherFunction(data: T) {
checkType<T>
}
however, if you cannot allow for an inline function (let's say in an interface!), you can kinda 'cheat' the system by saying, for example
class AmazingTypes {
inline fun <reified T> checkType(genericParameter: T) = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
}
fun myAwesomeMethod(someParameter: Any) {
val amazingClass = AmazingClass()
amazingClass.checkType(someParameter)
}
This is also example.
inline fun <reified T: ApiResponse> parseJson(body: String): T {
// handle OkResponse only
val klass = T::class.java
if (klass.isAssignableFrom(OkResponse::class.java)) {
return T::class.java.newInstance()
}
// handle others
return gson.from(body, T::class.java)
}