How to create method that takes generic object as parameter?
Like object of class Apple or Cycle. I don't want to downcast it from Any.
fun putObject(y: <T> /*and even only "T"*/) {
}
According to the documentation:
Not only classes can have type parameters. Functions can, too. Type parameters are placed before the name of the function:
fun <T> singletonList(item: T): List<T> {
// ...
}
You have to declare that T is a type parameter. You have to do it before the name of the function (like you would in Java). Rewriting your code as follows works:
fun <T> putObject(y: T) {
// ...
}
Related
I am trying to initialize an Object based on Kotlin Type parameters. The implementing class MyClass needs to have zero args constructor as it extends ActionCallback which does not allow parameters.
I have followed the Kotlin guide on inline functions to try and achieve this. The idea is that T is used on MyClass, which is used to instantiate myObject based on the Type. However I get error Cannot use 'T' as reified type parameter. Use a class instead.
Disclaimer: I do not know if inline functions is best way to approach this. I have used this route due to not being able to pass in parameters to MyClass but am open to other approaches.
inline fun <reified T> getObject(): T? {
return when (T::class) {
type1::class -> object1 as T?
type2::class -> object2 as T?
else -> null
}
}
class MyClass<T> : ActionCallback {
var myObject = getObject<T::class>() /*<--Error: Cannot use 'T' as reified type parameter. Use a class instead*/
}
I need to call the method suspend fun insert(e: T) by reflection declared as follows:
interface IMutableDao<T> {
#Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(e: T)
#Update
suspend fun update(e: T)
#Delete
suspend fun delete(e: T)
}
I tried with:
val insertFun = IMutableDao::class.functions.find {
it.name.equals("insert", true)
}
insertFun!!.callSuspend(dao, o)
But I get the exception "Callable expects 3 arguments, but 2 were provided.". I do not understand where the 3rd argument comes from.
UPDATE
I have found the problem. The 3rd is a Continuation instance. Does anyone know what to pass there ? I couldn't find any suitable instance.
UPDATE 2
The workaround I found was to create a temporary class inside the function I call the suspended function from, like this:
val c = object : Continuation<Unit> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
}
}
You are missing the "this" parameter which is the object that the method should be called with respect to.
it should be the first argument to the method.
You may use suspendCoroutine with Unit as type and use the continuation instance.
GlobalScope.launch {
suspendCoroutine<Unit> { continuation ->
insertFun!!.callSuspend(dao, o, continuation)
}
}
I would like to create a generic function that allows to cast context to activity type passed in parameter.
example of the idea:
private fun <T> castContext(activityType: T): T{
//example activityType = MainActivity
return context as T
}
For this to work you need to supply the type information, normally only available at compile-time (due to type erasure). In Java you would supply instance of Class or a something called type token.
1 If the type information is available at compile-time, you can use
private inline fun <reified T: Any> castContext(activity: Any?): T {
return activity as T
}
Inline function is a compile-time only construct and thus can 'embed' the type information into the bytecode in your stead (as in passing it explicitly as function parameter) - this is done by reifying the generic type parameter.
You could further narrow down the generic parameter bounds from Any to whatever you wish to specialize this function for your needs.
2 If you want to cast dynamically, to an instance of some class, unknown at compile-time, you need to do a normal cast:
val type: KClass<*> = ...
type.cast(instance)
type.safeCast(instance)
Because Kotlin's as and as? keywords are not methods (which irritates me to no end, due to often requiring extra () for cast, I'm using this pair of functions:
/** #return this as instance of the specified type (equivalent to this as T) */
inline fun <reified T: Any> Any?.asIs(): T = this as T
/** #return this as instance of the specified type (equivalent to this as? T) */
inline fun <reified T: Any> Any?.asIf(): T? = this as? T
The use of Any? as method receiver is somewhat controversial, due to handling null implicitly, instead of explicitly on call site, using ?.
How get the type of a Generic class and cast an object to it?
I want to use this function to pass an Interface class:
protected fun <T> getInteraction(): T {
return when {
context is T -> context
parentFragment is T -> parentFragment as T
else -> throw IllegalArgumentException("not implemented interaction")
}
}
and use it like:
private var interaction: ISigninInteraction? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
interaction = getInteraction<ISigninInteraction>()
}
Kotlin, Java and JVM do have types erasure when Generics are implemented. Generics are not visible at the bytecode level. It means, you cannot use type parameters, e.g. T, directly in the function code.
Kotlin adds support for reified generics, which helps here. You may declare the function like
inline fun <reified T> getIt() : T {
...
}
With the help of reified and inline it will be possible to cast to T and return it.
https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
The second alternative is to follow a Java practice - add the Class<T> parameter to the function and use Class#cast to cast to T instead.
You may combine the reified inline function with the approach like:
inline fun <reified T> getIt() = getIt(T::class.java)
I have a problem when I try to override a generics method with Boolean,Double,Integer,Float.
It works with Date. (May because is it Serializable?)
The interface:
interface AnInterface<C, T> {
fun doSomething(items: List<T>, vararg value: C): List<T>
}
An abstact implementation: (No override doSomething)
abstract class BaseClass<C, T> : AnInterface<C, T> { ... }
It's work:
class AnImplementetion<T> : BaseClass<Date, T>() {
override fun doSomething(items: List<T>, vararg value: Date): List<T> {
// It works
}
}
It doesn't work:
class AnAnotherImplementetion<T> : BaseClass<Boolean, T>() {
override fun doSomething(items: List<T>, vararg value: Boolean): List<T> {
// It doens't
}
}
The IDE always want to implement the doSomething. When I implement it with IDE it creates always the same one.
Error message:
Class 'AnAnotherImplementetion' is not abstract and does not implement abstract base class member
public abstract fun fun doSomething(items: List<T>, vararg value: Boolean): List<T> defined in BaseClass
'doSomething' overrides nothing
How can I fix it?
Thank you
UPDATE:
It works with JAVA. But Why doesn't with Kotlin?
public class AnAnotherImplementetion<T> extends BaseClass<Boolean, T> {
#NotNull
#Override
public List<T> doSomething(#NotNull List<? extends T> items, Boolean... value) {
// It works with JAVA
}
}
UPDATE 2:
It works when vararg is nullable.
interface AnInterface<C, T> {
fun doSomething(items: List<T>, vararg value: C?): List<T>
}
It looks like a bug in kotlin compiler. As I know during compiling it decides to use primitive type (int) or wrap (Integer). Java generics can't work with primitives, so compiler uses wrap for generic-type, BUT then compiler sees, that method param is never null and replaces it with primitive-type, and type-conflict appears. And here nullable saves a day.
But I'm not sure, it's just a guess.
The kotlin reference regarding basic types contains a passage which explains how it should deal with primitive types and generics in particular.
Obviously the latter is not working correctly. When generics are involved it should box the types which it either doesn't do or which the compiler complains about falsely.
You should open a bug at https://youtrack.jetbrains.com/ and link it here too. Maybe it was also a conscious design decision.
A workaround is to use the nullable type Boolean? as that will work as it is described in the reference. It will be boxed and therefore will work with generics.
Alternatively, if you are on the JVM, you can use the java.lang.Boolean instead. It's the object type of the primitive boolean and even though it is discouraged to use the Java types in Kotlin it is a possible workaround until Kotlin behaves as it should. However... testing it, Kotlin does some more magic around it so that its usage isn't that helpful neither. You would then even need to cast the java.lang.Boolean.TRUE as java.lang.Boolean. That's clearly not helpful at all. Opening a bug is the best you can do here.