Kotlin generic type class as function parameter - kotlin

How to pass parametr to function and then use it as generic type ?
private fun createRequest(data: Data, class: Class<out UploaderTask>): OneTimeWorkRequest {
return OneTimeWorkRequestBuilder<class>().build()
}

I found answer
private inline fun <reified T : UploaderTask>createRequest(data: Data): OneTimeWorkRequest {
return OneTimeWorkRequestBuilder<T>()
.setInputData(data)
.build()
}

Related

Ktor reified type parametar

I created class with generic in kotlin and want to use receive with generic, but I have error when i want to call.recieve type from generic:
Can not use MType as reified type parameter. Use a class instead.
Code:
class APIRoute<EType : IntEntity, MType : Any> {
fun Route.apiRoute() {
post {
val m = call.receive<MType>()
call.respond(f(model))
}
}
}
How to fix it?
You need to provide the expected type to the receive() function. Due to type erasure in Java/Kotlin, the type of MType is unknown at runtime, so it can't be used with receive(). You need to capture the type as KType or KClass object when constructing APIRoute.
KClass is easier to use, however it works with raw classes only, it doesn't support parameterized types. Therefore, we can use it to create e.g. APIRoute<*, String>, but not APIRoute<*, List<String>>. KType supports any type, but is a little harder to handle.
Solution with KClass:
fun main() {
val route = APIRoute<IntEntity, String>(String::class)
}
class APIRoute<EType : IntEntity, MType : Any>(
private val mClass: KClass<MType>
) {
fun Route.apiRoute() {
post {
val m = call.receive(mClass)
call.respond(f(model))
}
}
}
Solution with KType:
fun main() {
val route = APIRoute.create<IntEntity, List<String>>()
}
class APIRoute<EType : IntEntity, MType : Any> #PublishedApi internal constructor(
private val mType: KType
) {
companion object {
#OptIn(ExperimentalStdlibApi::class)
inline fun <EType : IntEntity, reified MType : Any> create(): APIRoute<EType, MType> = APIRoute(typeOf<MType>())
}
fun Route.apiRoute() {
post {
val m = call.receive<MType>(mType)
call.respond(f(model))
}
}
}

Pass a Java class reference as a function parameter in Kotlin

I want to pass a reference to a class to a function:
getCallData(ServiceCallBase::class.java)
fun getCallData(msg: Message, t: KClass<Any>): String {
return gson.fromJson((msg.obj as Bundle).getString(SERVICE_BUNDLE_KEY_DATA_TO_SERVICE), t)
}
The t parameter is not correct here. How do I correctly define the t parameter? The closest I can get is:
private inline fun <reified T> getCallData(msg: Message): String {
return gson.fromJson((msg.obj as Bundle).getString(SERVICE_BUNDLE_KEY_DATA_TO_SERVICE), T)
}
But here T is not an expression.
try this
inline fun <reified T> getCallData(msg: Message): String {
return gson.fromJson((msg.obj as Bundle)
.getString(SERVICE_BUNDLE_KEY_DATA_TO_SERVICE), T::class.java)
}
and use it like this :
getCallData<ServiceCallBase>(Message())
The problem is that ServiceCallBase is not a subtype of Any. You should change the signature to:
fun getCallData(t: KClass<*>) { ... }
Then, to call this function just use:
getCallData(ServiceCallBase::class)
But, to do that in more kotlin way use the second snipped you pasted:
inline fun <reified T> getCallData() { /* use T::class property here */ }
and call the function like this:
getCallData<ServiceCallBase>()

Type inference only works for extension function

The following code works fine and the call to the foo.get() extension function returns the correct type BarImpl.
open class Bar
class BarImpl: Bar()
class Foo<T : Bar>
inline fun <reified T : Bar> Foo<T>.get(): T {
return SomeMap(this).get(T::class)
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
But when I try to move Foo<T>.get() into the class the type inference fails
class Foo<T : Bar> {
inline fun <reified T : Bar> get(): T {
return SomeMap(this).get(T::class)
}
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
error: type inference failed: Not enough information to infer parameter T in inline fun get(): T
Please specify it explicitly.
val vm = foo.get()
^
How can I move the function into the class?
The extension function returns the result of the Foo type parameter. So the result type can be inferred from the receiver type.
And the member function result type has nothing in common with Foo type parameter except the name, which means nothing for a compiler. You can see that T in method and T in class are different types by writing and compiling the following code:
Foo<BarImpl>().get<BarImpl2>()
If you want to make get to be a member function which returns the result of Foo type parameter, you should remove type parameter from function and inject class instance via the constructor:
class Foo<T : Bar>(private val clazz: KClass<T>) {
fun get(): T {
return SomeMap(this).get(clazz)
}
companion object {
inline operator fun <reified T : Bar> invoke() = Foo(T::class)
}
}

why the translated kotlin code complains about a Array<BaseData>? to be a Array<out BaseData>

Having a java class, using androidStudio to translate to kotlin.
Got a error and not sure how to correctly translate it.
The java code:
public class BaseDataImpl extends BaseData {
private final BaseData[] translators;
public BaseDataImpl(final BaseData... translators) {
this.translators = cloneArray(translators);
}
public static <T> T[] cloneArray(final T[] array) {
if (array == null) {
return null;
}
return array.clone();
}
}
after the code translation, got error: required Array<BaseData>?, found Array<out BaseData>, but the translators in the cloneArray<BaseData>(translators) call is defined as val translators: Array<BaseData>?,
anyone could help to explain?
class BaseDataImpl(vararg translators: BaseData) : BaseData() {
private val translators: Array<BaseData>?
init {
this.translators = cloneArray<BaseData>(translators) //<=== error: required Array<BaseData>?, found Array<out BaseData>
}
companion object {
fun <T> cloneArray(array: Array<T>?): Array<T>? {
return array?.clone()
}
}
}
It is written in the Kotlin function reference regarding varargs:
Inside a function a vararg-parameter of type T is visible as an array of T, i.e. the ts variable in the example above has type Array<out T>.
where the referenced function was:
function <T> asList(vararg ts: T): List<T>
So in your case you actually pass an Array<out BaseData> but you only accept an array of type Array<T>? (in your case Array<BaseData>). Either you adapt all of the types to Array<out T> (which basically is similar as saying List<? extends BaseData> in Java) or you take care that you are only dealing with Ts instead, e.g. with:
inline fun <reified T> cloneArray(array: Array<out T>?): Array<T>? {
return array?.clone()?.map { it }?.toTypedArray()
}
But look up the documentation regarding this: Kotlin generics reference - type projections. You probably can accomplish this even easier.

Kotlin - How to find and cast an element by its type

I have a collection of objects which inherit Component and I want a function which finds an object by its concrete class and return it.
But Kotlin does not like the cast I do, and adding #Suppress("UNCHECKED_CAST") is ugly.
I have the following code:
open class GameObjectImpl : GameObject {
private val attachedComponents = mutableSetOf<Component>()
#Suppress("UNCHECKED_CAST")
override fun <TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.find { type.isInstance(it) } as? TComponent
}
}
This should work for you:
open class GameObjectImpl : GameObject {
val attachedComponents = mutableSetOf<Component>()
override inline fun <reified TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.filterIsInstance<TComponent>().firstOrNull()
}
}