How to cast generic type after isAssignableFrom check in Kotlin? - 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.

Related

mockk, how to verify a function is called with Map type and interface type

The class has a function:
fun theFunc(uri: Uri, theMap: Map<String, String>?, callback: ICallback) {
......
}
and would like to verify it is called with proper params type
io.mockk.verify { mock.theFunc(ofType(Uri::class), ofType(Map<String, String>::class), ofType(ICallbak::class)) }
the ofType(Uri::class) is ok,
the ofType(Map<String, String>::class got error:
the ofType(ICallbak::class) got error:
ICallback does not have a companion object, thus must be initialized
here.
How to use the ofType() for Map and interface?
The problem is that generic parameters are lost at runtime due to type erasure, and for this reason the syntax doesn't allow generic parameters to be specified in that context. You can write Map::class but not Map<String, String>::class because a Map<String, String> is just a Map at runtime.
So, you can call it like this:
verify { mock.theFunc(ofType(Uri::class), ofType(Map::class), ofType(ICallback::class)) }
that will work. However, there is also a version of function ofType which takes generic parameters, so you can use this:
verify { mock.theFunc(ofType<Uri>(), ofType<Map<String, String>>(), ofType<ICallback>()) }
You need to use mapOf<String,String>::class
io.mockk.verify { mock.theFunc(ofType(Uri::class), ofType(mapOf<String,String>()::class), ofType(ICallbak)) }
For interface, you can create mocck object. And put it into ofType.
val callbackMock: ICallback = mockk()
io.mockk.verify { mock.theFunc(ofType(Uri::class), ofType(mapOf<String,String>()::class), ofType(callbackMock::class)) }

Kotlin Factory Class with Generic outputs

I'm in the process of trying to port some code I wrote in Java over to Kotlin and I'm struggling mightily with some issues around generics. I quite commonly use a factory pattern in Java to return an instance of a generic interface that I want to call for a given type.
In Java I had this contract:
public Message<T extends Action> {
private List<T> actions;
..some other properties
}
And this interface:
public interface MessageConverter<T extends Action, M extends BaseModel> {
List<M> convertMessage(Message<T> message);
DataType getDataType();
}
And lastly this factory:
public class MessageConverterFactory {
//This gets populated via DI
private Map<DataType, MessageConverter> converterMap;
public <T extends Action, M extends BaseModel> MessageConverter<T, M> getMessageConverter(DataType dataType) {
return converterMap.get(dataType);
}
}
With all that in place, I was able to do things like this:
Message<T> message = mapper.readValue(messageString, type);
MessageConverter<T, M> messageConverter = messageConverterFactory.getMessageConverter(dataType);
List<M> dataModels = messageConverter.convertMessage(message);
I understand that I was abusing raw generic types in Java to an extent to make this happen, but I assumed there would be some way to still do a generic factory pattern like this.
However, no matter with I try with generic variance, star projections, etc. I cannot get Kotlin to accept any version of this code. The closest I got was down to the invocation of the generic converter's convertMessage call. It was failing because I was using star projections and attempting to restrict the type of T, but that was leading to the compiler thinking convertMessage accepts Message<Nothing>.
Is code like this possible in Kotlin? Or is there a similar alternative approach I should be using instead?
Thanks,
Jeff
The literal conversion of this to Kotlin is pretty simple, and the Java-to-Kotlin converter built in to IDEA would spit something like this out almost directly, given the equivalent Java code:
class Message<T: Action> {
private val actions: List<T> = TODO()
...
}
interface MessageConverter<T: Action, out M: BaseModel> {
fun convertMessage(message: Message<T>): List<M>
val dataType: DataType
}
class MessageConverterFactory(val converterMap: Map<DataType, MessageConverter<*, *>>) {
fun <T: Action, M: BaseModel> getMessageConverter(dataType: DataType): MessageConverter<T, M> {
return converterMap[dataType] as MessageConverter<T, M>
}
}
Note, the cast in getMessageConverter -- your Java code is doing the equivalent, without being explicit about it -- I believe the compiler would even spit out a warning about an unchecked assignment.
An alternative in Kotlin is to use an inline function with reified types to return the appropriate converter. For example, something like this:
inline fun <reified T: Action, reified M: BaseModel> converterOf(): MessageConverter<T, M> = when {
T::class == FooAction::class, M::class == BarModel::class -> TODO()
else -> error("No converter available for type ${T::class.simpleName} to ${M::class.simpleName}")
}

Kotlin generic interface resolver

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

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)
}