Lanky Dan Blog

Kotlin primitive and object arrays

June 21, 2019

kotlinjavabeginner

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,

public class PrimitiveArrays {

  public static void main(String args[]) {
    Long[] longArray = new Long[] {1L, 2L, 3L};
    takesInPrimitiveLongArray(longArray);
  }

  static void takesInPrimitiveLongArray(long[] array) {}
}

This does not work and attempting to compile it gives the following error:

error: incompatible types: Long[] cannot be converted to long[]
    takesInPrimitiveLongArray(longArray);

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:

val array = Array<Long>(3)
// or
val array: Array<Long> = arrayOf(1,2,3)

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 long[].

This situation is somewhat unique to arrays. A Kotlin Long used by itself can compile to either a Long or 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:

Kotlin Java
ByteArray byte[]
CharArray char[]
ShortArray short[]
IntArray int[]
LongArray long[]
DoubleArray double[]
FloatArray float[]
BooleanArray boolean[]

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:

@file:JvmName("PrimitiveArrays")
package dev.lankydan

fun main(args: Array<String>) {
  // long and Long arrays
  val longArray = longArrayOf(1,2,3,4)
  val arrayOfLongs = arrayOf<Long>(1,2,3,4)
  // int and Integer arrays
  val intArray = intArrayOf(1,2,3,4)
  val arrayOfInts = arrayOf<Int>(1,2,3,4)
  // boolean and Boolean arrays
  val booleanArray = booleanArrayOf(true, false)
  val arrayOfBooleans = arrayOf<Boolean>(true, false)
  // char and Character arrays
  val charArray = charArrayOf('a','b','c')
  val arrayOfChars = arrayOf<Char>('a', 'b', 'c')
}

Using Intellij’s Kotlin bytecode decompiler, the snippet decompiles to:

public final class PrimitiveArrays {
   public static final void main(String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      // long and Long arrays
      long[] var10000 = new long[]{1L, 2L, 3L, 4L};
      Long[] var9 = new Long[]{1L, 2L, 3L, 4L};
      // int and Integer arrays
      int[] var10 = new int[]{1, 2, 3, 4};
      Integer[] var11 = new Integer[]{1, 2, 3, 4};
      // boolean and Boolean arrays
      boolean[] var12 = new boolean[]{true, false};
      Boolean[] var13 = new Boolean[]{true, false};
      // char and Character arrays
      char[] var14 = new char[]{'a', 'b', 'c'};
      Character[] var15 = new Character[]{'a', 'b', 'c'};
   }
}

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, LongArray becomes long[] and Array<Long> becomes Long[].

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 😥.

If you found this post helpful, you can follow me on Twitter at @LankyDanDev to keep up with my new posts.


Dan Newton

Saving transactions where only a subset of parties are signers

July 05, 2019
cordakotlindltdistributed ledger technologyblockchain

It took a while for me to think of a title that could summarise the contents of this post without becoming a full sentence itself. I think I…

Preventing invalid spending of broadcasted states

June 12, 2019
cordakotlindltdistributed ledger technologyblockchain

Corda is super flexible and will allow you to put together the code needed to write many complex workflows. This flexibility does come with…

Broadcasting a transaction to external organisations

May 31, 2019
cordakotlindltdistributed ledger technologyblockchain

There is a misconception that Corda cannot broadcast data across a network. This is simply wrong. In fact, Corda can send anything between…

Running a Kotlin class as a subprocess

May 25, 2019
kotlin

Last week I wrote a post on running a Java class as a subprocess . That post was triggered by my need to run a class from within a test…

Extending Flows to customise transaction validation

May 18, 2019
cordacorda 4kotlindltdistributed ledger technologyblockchain

Through the use of flow extension, nodes running the same CorDapp can include extra validation to ensure that a transaction meets their…