IDEA warns me that Java boxed types shouldn’t be used, and that I should use respective Kotlin types instead. It works most of the time, but sometimes leads to unexpected results. Here’s an example.
class Property<T> private constructor(val type: Class<T>) {
companion object {
@JvmField val STRING_PROPERTY = Property(String::class.java)
@JvmField val INT_PROPERTY = Property(Int::class.java)
@JvmField val LONG_PROPERTY = Property(Long::class.java)
}
}
class ClassWithProperties {
private val properties = HashMap<Property<*>, Any>()
fun <T : Any> setProperty(property: Property<T>, value: T) {
properties[property] = value
}
fun <T : Any> getProperty(property: Property<T>): T = property.type.cast(properties[property])
}
This compiles, as well as the following Java code:
ClassWithProperties o = new ClassWithProperties();
o.setProperty(Property.STRING_PROPERTY, "string");
o.setProperty(Property.INT_PROPERTY, 12345);
o.setProperty(Property.LONG_PROPERTY, 12345L);
o.getProperty(Property.STRING_PROPERTY);
o.getProperty(Property.INT_PROPERTY);
o.getProperty(Property.LONG_PROPERTY);
However, it throws at runtime: java.lang.ClassCastException: Cannot cast java.lang.Integer to int
. Well, that’s expected because I use Java reflection to cast and Java knows nothing of Kotlin types. The equivalent code in Kotlin throws the same exception, no surprise here too. To make it work, I can change the code like this:
class Property<T> private constructor(val type: Class<T>) {
companion object {
@JvmField val STRING_PROPERTY = Property(String::class.java)
@JvmField val INT_PROPERTY = Property(Integer::class.java)
@JvmField val LONG_PROPERTY = Property(java.lang.Long::class.java)
}
}
Now it works just fine, but looks ugly enough already. The calling code looks even more ugly in Kotlin:
val o = ClassWithProperties()
o.setProperty(Property.STRING_PROPERTY, "string")
o.setProperty(Property.INT_PROPERTY, 12345 as Integer)
o.setProperty(Property.LONG_PROPERTY, 12345L as java.lang.Long)
o.getProperty(Property.STRING_PROPERTY)
o.getProperty(Property.INT_PROPERTY)
o.getProperty(Property.LONG_PROPERTY)
This, of course, generates warnings about Java boxed classes used in Kotlin. I can just suppress them, but still. Java code, on the other side, works just fine without any warnings or errors, just as expected.
Is this an unavoidable problem with Java interop? Of course, if generics in Java used reified types to begin with, like .Net does, it wouldn’t be a problem at all. But since it’s not the case, I’m trying to emulate reified types with reflection. Should I just suppress those warnings and bear with it? Is it possible to do the same thing in a more elegant way?