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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With