Problems with Java raw type wrappers


#1

I have been using Kotlin for 2 years and never had this problem, but now I have met TensorFlow and a new problem has appeared.

The problem resides in a class of TensorFlow called org.tensorflow.DataType. This class converts Java’s raw type wrappers (such as Integer or Float) into TensorFlow types INT32, FLOAT.

It works by storing Wrapper classes in a map of <Class<?>, DataType>.

link to its source

So whenever some code call this from Kotlin:
DataType.fromClass(Int::class.java)

or this from java:
DataType.fromClass(int.class);

An IlegalArgumentException is thrown saying: “int objects cannot be used as elements in a TensorFlow Tensor”, so the only way to do it work from is with a wrapper class like:
DataType.fromClass(java.lang.Integer::class.java)

This is not a problem in Java, due to the auto-casting, but in Kotlin, it forces you to migrate from Kotlin basic types to Java types, this is horrible, because kotinc sees all Java compiled sources primitive types as Kotlin basic types, so your project becomes a complete mess, with tons of code for changing from kotlin basic types to Java primitive types and vice-versa.

In this case, the problem is easily solved with reflection, but the problem would be bigger if the values in the HashMap were hard-coded with a switch.

So what should we do for other cases if the problem can not be solved just with reflection?


#2

Unless I’m misunderstanding you, this is not true. Kotlin will box those for you automatically for anywhere expecting an object (e.g. in a map’s put). Kotlin’s Int can be seen as Java int where primitives can be used or java.lang.Integer where they can’t (e.g. nullable and objects). What call are you making where you are solving this with reflection instead of letting autoboxing do its thing?

Now, when referencing the literal java.lang.Class, yes, you have to differentiate between boxed and non-boxed.


#3

Kotlin auto-casting is great unless you are using java types in kotlin, ej:

fun example(i: java.lang.Integer){

}

can not be called with:

example(1) //Wont compile, it claims "The integer literal does not conform the expected type integer"

From Kotlin, it can be called like this from Java, and a Java code with that type can be called like this from kotlin.

The problem resides if you use Java types in your Kotlin code, not if you use java compiled code with Java types.

Kotlin is not intended to use Java types, but this strange approach in TensorFlow forces you to use them.

The reflection is for adding primitives to DataType’s map, it’s for modifying a TensorFlow class, but this just will work for this case, it is not a global solution.


#4

But why do you use java.lang.Integer as the parameter type of your functions? Just use Int as the type on your end and use DataClass.FromClass(java.lang.Integer::class.java) to create the DataClass.


#5

Or more specifically, Int? as the parameter which will compile to java.lang.Integer. So you have:

fun example(i: Int?) {

}

#6

Firstly, is DataType, and this is a TensorFlow class, not a class from my project.

I can not use kotlin types in the project, because kotlinc will convert them to java primitive types, and then, TensorFlow will try to convert those types into TensorFlow types, but it will fail, because DataType’s map does not contain java primitive types, only java wrapper types.


#7

This is not true. Sometimes kotlinc will convert them to java primitive wrapper types, e.g. Int? becomes java.lang.Integer (and sometimes Int becomes java.lang.Integer in object situations like generics).


#8

Yes, in that case, but not when referencing the class.

Let’s say, kotlinc compiles Int to Java’s int, but it does not compile Int::class.java to java’s Integer.class. that’s the problem.


#9

So if there is a map of Integer.class as key, Int::class.java won’t retrive the value.


#10

What’s wrong with manually typing java.lang.Integer::class.java (or Int::class.javaObjectType)? I guess I don’t understand the bug? You are confusing the actual integer with the type. If there is a map with Integer class as a key, why can’t you do map.get(Int::class.javaObjectType)? Or if it’s a generic value, someValue::class.javaObjectType. Why’s that a problem?


#11

That solved my problem.

The bug is in java - kotlin converter, it converts Integer.class to Int::class.java instead of Int::class.javaObjectType.

I didn’t realize about that when checking my code who was originally in java.

Thank you very much for your help.