Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting generic type on Kotlin

I'm trying to make mapOf() returns EnumMap if K is Enum. However, the error occurs at first type variable of Enum (K)

Code:

@kotlin.internal.InlineOnly
public inline fun <reified K, V> mapOf(): Map<K, V> =
    if (K::class.java.isEnum) EnumMap<K, V>(K::class.java)
    else emptyMap()

Error:

Type argument is not within its bounds.
Expected: Enum<K!>!
Found: K

I'd like to give objects other than Enum too, so reified K: Enum<*> can't be a solution.

like image 784
Mon_chi Avatar asked Apr 12 '18 12:04

Mon_chi


2 Answers

Evil cheating using type erasure:

import java.util.concurrent.TimeUnit // you can use any other enum as well

@Suppress("UNCHECKED_CAST")
public inline fun <reified K, V> mapOf(): Map<K, V> =
    if (K::class.java.isEnum) 
        EnumMap<TimeUnit, V>(K::class.java as Class<TimeUnit>) as Map<K, V>
    else emptyMap()

At runtime this is effectively just EnumMap(K::class.java), all casts disappear.

like image 93
Alexey Romanov Avatar answered Sep 23 '22 11:09

Alexey Romanov


There is no clean way to make this work (afaik), since making K conform to the bound K extends Enum<K> (required for EnumMap) would require the dynamic adding of a bound to K, which is impossible, since generics are a compile time mechanism.

In java, you would be able to construct the EnumMap as a raw type, and have it be converted unchecked to the target type (i.e. asserting it is correct). But Kotlin doesn't allow you to do that, since it doesn't allow raw types. I can think of one way to get around this limitation, which is reflection:

val constructor = EnumMap::class.constructors.find {
    con ->
        con.parameters.size == 1
        && con.parameters[0].type.jvmErasure == Class::class
}!!

inline fun <reified K, V> mapOf(): Map<K, V> =
    if (K::class.java.isEnum) constructor.call(K::class.java) as Map<K, V>
    else emptyMap()

My Kotlin isn't the best, so there may still be room for improvement. This is a possible workaround though.

like image 29
Jorn Vernee Avatar answered Sep 22 '22 11:09

Jorn Vernee