Kotlin : Casting an object to Generic class - android

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)

Related

How to instantiate an Object based on Type Parameter in Kotlin?

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*/
}

pass a class as a parameter to class than calling enumValues with the class that passed

I am trying to pass a value (class) to another class, and in that class I will use the parameter(the passed class) with enumValues<parameter>()
I have tried passing generic MyClass<T>()
and than enumValues<T>()
but I am getting "cannot use T as a reified , use class instead".
Depending on the context of where you're using this, you can simply declare the method as inline and reified:
inline fun <reified T : Enum<T>> useEnumValues() {
val values = enumValues<T>()
}
Otherwise, if you require the class reference:
fun <T : Enum<T>> useEnumSet(enumClass: Class<T>) {
val values = EnumSet.allOf(enumClass)
}
If your Class value is called cls, just call cls.enumConstants.

How to make a generic Type cast function to cast context to Any activity?

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

Kotlin Inheritance with Boolean generics

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.

Convenient Kotlin LoggerFactory simplification

What is the most convenient way to use SLF4J or other logging approaches with kotlin?
Usually the developer is busy with boilerplate code like
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
in each and every class to get a proper logger?
What are the most convenient ways to unify/simplify this with Kotlin?
You can define an extension property on every type:
val <T : Any> T.logger: Logger
get() = LoggerFactory.getLogger(this::class.java)
use it as follows:
class X {
init {
logger.debug("init")
}
}
Here's a simple example which returns a lazily-initialized logger from a bound callable reference or a standard property. I prefer calling from a callable reference because the :: denotes reflection (related to logging).
The class which provides the Lazy<Logger>:
class LoggingProvider<T : Any>(val clazz: KClass<T>) {
operator fun provideDelegate(inst: Any?, property: KProperty<*>) =
lazy { LoggerFactory.getLogger(clazz.java) }
}
Inline functions to call them:
inline fun <reified T : Any> KCallable<T>.logger() =
LoggingProvider(T::class)
inline fun <reified T : Any> T.logger() =
LoggingProvider(T::class)
Here's an example of using them. The require assertion in the initializer shows that the loggers share a reference:
class Foo {
val self: Foo = this
val logger by this.logger()
val callableLogger by this::self.logger()
init {
require(logger === callableLogger)
}
}
I define this function in my projects to make defining a logger easier for me. It takes advantage of Kotlin's reified types.
// Defined in Utilities.kt
inline fun <reified T:Any> logFor() =
LoggerFactory.getLogger(T::class.java)
Usage:
class MyClass {
private val log = logFor<MyClass>()
...
}
Or if you are creating a lot of them:
class MyClass {
companion object {
private val log = logFor<MyClass>()
}
...
}
if you don't like the boilerplate, you can always wrap the log.info with your own logger helper:
mylog.info(this, "data that needs to be logged")
Then in the background, have some sort of hashmap that keeps track of classes of the this param that can instantiate a logger for that class.
Other options might be using AspectJ Weaving to weave a logger into each class, but this is overkill in my opinion.
I have defined a utility method for this
fun getLogger(cl: KClass<*>): Logger {
return LoggerFactory.getLogger(cl.java)!!
}
and now in each class I can use the logger like this
companion object {
private val logger = getLogger(MyClass::class)
}

Categories

Resources