I initially set out to write this post because I was playing around with some reflection code and thought I found something interesting. Alas, that was definitely not the case. Instead, it was just a basic feature of Kotlin that I haven’t needed to use or focus on yet. Although this post didn’t turn out the way I wanted it to be, I still think it is a nice little post to bring some clarity to this subject.
In Java, there is the concept of primitive types and their wrapped versions. Thanks to autoboxing and unboxing, types can be interchanged between their primitive and wrapped versions. In other words, in most situations you can use a
long instead of a
Long or a
Long instead of a
long. If you didn’t notice where the capitals were in that last sentence then I imagine it probably looked quite confusing. The wording in that sentence is also crucial. More specifically, the statement “in most situations”.
Autoboxing and unboxing does not work when attempting to interchange a primitive array and a wrapped (
Object) array. For example,
This does not work and attempting to compile it gives the following error:
Switching the method to take in
Long and passing in a
long will also fail to compile for the same reasons. This is not something that most Java developers will find interesting but helps set the groundwork for the actual content of this post.
Kotlin needs to provide you with the equivalent of Java’s primitive arrays. But, Kotlin does not let you define arrays using the same syntax as Java. In Kotlin initialising an array looks like:
The fact that you can see the
Array uses generics should highlight that it is not a primitive array. This is a fact in both Java and Kotlin, that generic types cannot be primitives. Otherwise, it could be switched out for
Array<long>, and we would all be happy. The code above compiles down to an object array of
Long instead of a primitive
This situation is somewhat unique to arrays. A Kotlin
Long used by itself can compile to either a
long in JVM bytecode. The compiled type depends on the nullability of the field. Arrays are more explicit, so their types won’t change when compiled.
To circumvent this, Kotlin provides a selection of classes that become primitive arrays when compiled down to JVM bytecode.
These classes include:
There are also further classes for arrays of unsigned types.
These classes can also be interchanged between Kotlin and Java without any extra effort.
As a final piece of evidence showing you the differences between primitive and wrapped/object arrays in Kotlin, I want to show you some Kotlin code that is converted to its Java counterpart:
Using Intellij’s Kotlin bytecode decompiler, the snippet decompiles to:
Firstly, note that Kotlin provides you with useful initialisation functions for your arrays. Both for primitive and object arrays. Secondly, how they are compiled. For example,
You can now see the differences between these arrays. But, I have not mentioned which ones you should be utilising. You should defer to primitive types in the same way that Java does. This is due to the performance impact that autoboxing and unboxing can have on your application.
For smaller workloads, the result is likely to be negligible. On the other hand, for larger arrays in performance critical applications, this possibly small change can have a noticeable effect. Some more information on this subject can be found here.
If you need to store nulls in your arrays, then you will still need to refer back to a wrapped/object array. In most situations, I think you should be able to utilise primitive arrays, but there are always going to be times when you can’t. That being said, most of the time we all just use
Lists, so none of this really matters.
You should now have a better understanding of the differences between primitive arrays like
LongArray and object arrays such as
Array<Long>. If not, then I have failed you, and I apologise for that.