kotlin: list properties of any object - variance error - kotlin

I'm trying to write a function that produces map of properties and values for any type
inline fun <reified T : Any> T.propertiesMap() {
for (property in this::class.memberProperties) {
property.get(this)
}
}
i get a compilation error in property.get(this) about
out-projected type [...] prohibits the use of 'public abstract fun get(receiver...

The issue is that this::class produces a KClass<out T> instead of KClass<T> which is what would be needed to use anything of type T in the property.get(...) call. So you can do an unchecked cast to do what you want:
fun <T : Any> T.propertiesMap() {
#Suppress("UNCHECKED_CAST")
for (property in (this::class as KClass<T>).memberProperties) {
property.get(this)
}
}
Which does not require the function to be inline nor reified type parameter. Otherwise you can change your function to use T::class instead of this::class to create a matching KClass<T>.
inline fun <reified T : Any> T.propertiesMap() {
for (property in T::class.memberProperties) {
property.get(this)
}
}

If you use the type you are reifying rather than an instance of that type the variance issue will go away. When you call T::class.memberProperties you get back a Collection<KProperty<T, *>> which is what I believe you want. On the other hand, if you call that on an instance (this) rather than a type, you get back a Collection<KProperty<out T, Any?>>, which is where your out-variance issue comes from.
inline fun <reified T : Any> T.propertiesMap() {
for (property in T::class.memberProperties) {
property.get(this)
}
}
Essentially, you need to do T::class rather than this::class in order to get the right kind of collection back. I've left your code as-is otherwise because I'm not clear on what you want this function to do, but I suspect you could drop the for loop in favor of a map call.

Related

Why do i need to specify constructor argument as nullable generic type?

I'm trying to make OK() to call ApiResponse constructor. When I give null to ApiResponse constructor argument, it shows error that type mismatches.
If I change data type to T? it works. Why is it happening? Default upper bound of T is Any? so i thought it won't be any problem to assign null.
class ApiResponse<T> private constructor(
val data: T, // If I change data type to T?, no error
val message: String?
) {
companion object {
fun <T> OK(): ApiResponse<T> {
return ApiResponse(null, null)
}
fun <T> OK(data: T): ApiResponse<T> {
return ApiResponse(data, null)
}
}
}
I've searched with keywords kotlin, generic, constructor, nullable, T but i could not find answer.
In
fun <T> OK(): ApiResponse<T> {
return ApiResponse(null, null)
}
if someone calls ApiResponse.OK<String>(), then it tries to construct an ApiResponse where data is null and also of type String, which is incompatible. None of your types prevent that call -- when you have a generic type argument to the function like that, the caller can specify any T they please, including a nonnull type.
You must either return an ApiResponse<T?>, or not have an argumentless OK factory method.

Kotlin generic factories

I'm trying to create an AnimalFactory that returns generic factories for making different types of Animals, depending on the arguments passed to the AnimalFactory.
Here's the code:
interface Animal {
fun talk(): String
}
class Cow: Animal {
override fun talk(): String {
return "mooo"
}
}
class Cat: Animal {
override fun talk(): String {
return "miow"
}
}
class Dog: Animal {
override fun talk(): String {
return "bark"
}
}
object AnimalFactory {
fun <T: Animal> AnimalMakerFactory(type: String): AnimalMaker<T> {
val maker = when (type) {
"cat" -> CatMaker()
"dog" -> DogMaker()
else -> CowMaker()
}
return maker
}
}
interface AnimalMaker<out T: Animal> {
fun make(): T
}
class CatMaker: AnimalMaker<Cat> {
override fun make(): Cat {
return Cat()
}
}
class DogMaker: AnimalMaker<Dog> {
override fun make(): Dog {
return Dog()
}
}
class CowMaker: AnimalMaker<Cow> {
override fun make(): Cow {
return Cow()
}
}
I get a type exception:
Type mismatch.
Required: AnimalMaker<T>
Found: AnimalMaker<Animal>
I thought that AnimalMaker would solve this, but apparently not. Why is AnimalMaker<T> not of type AnimalMaker<Animal> here?
The return value of the function is AnimalMaker<T> and not AnimalMaker<Animal> because that’s what you declared as the return type. The variable maker is indeed an AnimalMaker<Animal> but that isn’t a match for what the function is supposed to return because T could be a subtype of Animal.
You declared your function as having a generic type of T: Animal. Generic types are always an input to the function. In this case, it doesn’t make sense to use a generic input to the function because there’s no way to enforce that the type given is a match for the input String it corresponds with. To make your function work, you can remove <T : Animal and declare that it returns AnimalMaker<Animal>.
A little more explanation. There are two reasons why you might want to use generics in a function signature.
Enforce input parameter types.
Determine the output type.
You might use generics for one or both reasons (but the second can only be done by itself in a useful way by using reified generics, except in very specific cases where the returned class won’t be producing anything).
In your case, your input generic is not used to enforce the input parameter since that is just a String. To use it for the second reason, you would have to cast your return value’s type to the unknown (to the compiler) type T which would be unsafe because there’s no way to know if the input type given at the call site is a valid match for the given input String. And if you expected the call site to pass the right type, it would be redundant and error prone to also require a matching String to be passed.
Edit:
If you know the input type at compile time, then you can do this with reified generics. Get rid of the String input. It would look like this:
object AnimalFactory {
inline fun <reified T: Animal> AnimalMakerFactory(): AnimalMaker<T> {
#Suppress("UNCHECKED_CAST")
return when (T::class) {
Cat::class -> CatMaker()
Dog::class -> DogMaker()
Cow::class -> CowMaker()
else -> error("No factory found for type ${T::class}.")
} as AnimalMaker<T>
}
}
// Example usage
val someCatFactory = AnimalFactory.AnimalFactoryMaker<Cat>()
val cat: Cat = someCatFactory.make()
Inside this function, it is up to you to match the types up correctly, or there will be a ClassCastException at runtime. It seems logically it should be able to automatically cast them, but the compiler isn't sophisticated enough (yet?).

Map return type from input generic type in Kotlin

I have a function that returns IMyInterface
fun getValue(type: Types): IMyInterface? {}
But I have to always cast the return type in this way before I can use it:
getValue(Types.TypeInt)?.let { value ->
val usableVale = MyInterfaceAsInt.cast(value)
// more code...
}
MyInterfaceAsInt implements IMyInterface and I have no control over them.
The casting always depend of the input, so
Types.TypeInt -> MyInterfaceAsInt.cast(value)
Types.TypeLong -> MyInterfaceAsLong.cast(value)
...etc
Is there a way to define somthing like fun <T = Types> getValue(type: T) in a way that the return type can be inferred from type ?
I would like to do the casting inside getValue.
It looks like Types.TypesInt/Long/etc. are simply instances of the same type Types, not different types; and in fun <T> getValue(type: T), T has to be a type. So it doesn't seem to be possible.
But I would probably go the other way and define functions like
fun getValueAsInt(): MyInterfaceAsInt? = getValue(Types.TypeInt)?.let { MyInterfaceAsInt.cast(it) }
fun getValueAsLong(): MyInterfaceAsLong? = getValue(Types.TypeLong)?.let { MyInterfaceAsLong.cast(it) }
...
Another alternative which could be useful at least when the type can be inferred:
#Suppress("UNCHECKED_CAST")
inline fun <reified T : MyInterface> getValue(): T? = when(T::class) {
MyInterfaceAsInt::class -> getValue(Types.TypeInt)?.let { MyInterfaceAsInt.cast(it) }
MyInterfaceAsLong::class -> getValue(Types.TypeLong)?.let { MyInterfaceAsLong.cast(it) }
...
} as T

Do we still need Class (or KClass) after having Reified?

With reified, I could replace the below function
private fun <T>createFragmentX(fragmentClass: Class<T>, fragmentArgs: Bundle?): Fragment {
try {
val fragment = fragmentClass.newInstance() as Fragment
fragment.arguments = fragmentArgs
return fragment
} catch (exception: Exception) {
throw RuntimeException(exception.message)
}
}
with this
private inline fun <reified T>createFragmentZ(fragmentArgs: Bundle?): Fragment {
try {
val fragment = T::class.java.newInstance() as Fragment
fragment.arguments = fragmentArgs
return fragment
} catch (exception: Exception) {
throw RuntimeException(exception.message)
}
}
It looks to me, with reified there's no longer need of using Class or KClass. Am I right?
Is there other use of Class or KClass that can't be fulfilled by reified, other than wanted to do pure reflection work?
Short answer:
In your example, it does not make a difference, except for aesthetics.
Long answer:
If you pass in T as reified, you can retrieve the KClass from the reified type parameter T like this
val kClass = T::class
at any time. You then have all the options you would have using a KClass as parameter, so no difference here.
There are situations where you need to use KClass as parameter though. Here a two:
1) No inline
When you don't want your function to be inline, because using a reified type works only with inlined functions.
2) Default values
Another situation would be default values:
fun f(kClass: KClass<*> = Int::class) { /*...*/ }
You couldn't achieve exactly the same with a reified type parameter.
Conclusion:
In all other cases go for the generic type parameter, because it makes the call-site more concise as shown here (which may be personal preference):
inline fun <reified T> printType() {
println(T::class.simpleName)
}
fun printType(kClass: KClass<*>) {
println(kClass.simpleName)
}
Call-site
printType<Int>() // better
printType(Int::class)
You don't need to pass it as a parameter here, but your code still uses Class: that's what T::class.java is. So yes, there are many cases where you need Class and yours is one of them.

How do I call a method in Kotlin with a different upper bound?

e.g. Given a Class<T> how do I call/invoke a method/constructor that requires Class<T> where T : Enum<T>?
fun <T : Any> handleAny(classOfT: Class<T>) {
if (classOfT.isEnum) {
handleEnum(classOfT)
}
}
fun <T : Enum<T>> handleEnum(classOfT: Class<T>) { /*...*/ }
Error: inferred type T is not a subtype of kotlin.Enum<T>
In Java I can do an unchecked call but I cannot seem to find a way to do anything similar in Kotlin.
As for now I found this quite hacky workaround for it:
private enum class DummyEnum
fun <T> handleAny(classOfT: Class<T>) {
if (classOfT.isEnum) {
handleEnum(classOfT as Class<DummyEnum>) //absolutely any enum goes
}
}
fun <T : Enum<T>> handleEnum(classOfT: Class<T>) {
println(classOfT.toString())
}
The idea is to make an unchecked cast to the type with any generic parameter satisfying the upper bound (let it be DummyEnum), which will then be erased at runtime anyway.
The limitation is that the solution doesn't work correctly with reified generics: if handleEnum had reified type parameter, it would be substituted for statically inferred type (DummyEnum).