How to pass Scala arrays back and forth with Java methods

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 11.12, “How to Pass Arrays Back and Forth with Java Methods”

Problem

You want to pass an array back and forth between your Scala and Java methods.

Solution

The first link in the See Also section states, “Scala arrays correspond one-to-one to Java arrays,” and as a result of this, passing arrays back and forth between Java and Scala code is easier than the example shown in Recipe 17.1, “Going to and from Java Collections”.

For instance, if you have a Java class with methods that return an int[] or String[], like these:

// java
package javaarrays;
public class Test {
    public static int[] getNumbers() {
        int[] x = {1, 2, 3};
        return x;
    }
    public static String[] getStrings() {
        String[] x = {"a", "b", "c"};
        return x;
    }
}

you can call them directly from your Scala application:

package javaarrays
object ArrayTest extends App {
    Test.getNumbers.foreach(println)
    Test.getStrings.foreach(println)
}

If you look at the types in your Scala code, you can see how Scala sees them:

println(Test.getNumbers.getCanonicalName)
// int[]

println(Test.getStrings.getClass.getCanonicalName)
// String[]

You can also access a Scala Array from a Java application. Given this Scala code that builds a method named getFriends:

package javaarrays
object ArrayTest {
    // create one Person
    val fred = Person("Fred")
    val fredsFriends = Array(Person("Barney"), Person("Betty"), Person("Wilma"))
    fred.friends = fredsFriends

    // create a second Person
    val barney = Person("Barney")
    val barneysFriends = Array(Person("Betty"), Person("Fred"), Person("Wilma"))
    barney.friends = barneysFriends

    // return the two Person instances when called
    def getFriends = Array(fred, barney)
}

case class Person(name: String) {
  var friends = Array[Person]()
  override def toString = s"$name (${friends.mkString(",")})"
}

you can use the following Java class to call the getPerson method and print out the Person instances:

package javaarrays;

public class GetScalaArrays {
    public static void main(String[] args) {
        Person[] peeps = ArrayTest.getFriends();
        for (Person p: peeps) {
            System.out.println(p);
        }
    }
}

As long as you’re working with an Array on the Scala side and treating it as an array of objects in Java, this works fine.

Discussion

In the book, Programming in Scala, the authors discuss an unusual situation that can happen with legacy Java code. One approach to having an array of objects in Java where each object was a subtype of some supertype was to define the array as Object[]. This is shown in the following Java code, where the method printObjectArray expects an array of Object:

// java
package javaarrays2;
public class AJavaClass {
    public static void printObjectArray(Object[] objects) {
      for (Object o: objects) {
          System.out.println(o);
      }
    }
}

Attempting to call this Java method from Scala with an array of type Any will not work:

val objects = Array("a", 1)
AJavaClass.printObjectArray(objects)  // will not compile

For that matter, an array of String also won’t work:

val objects = Array("a", "b")
AJavaClass.printObjectArray(objects)  // will not compile

The solution to this problem is to cast your Scala array to an Array[Object], as shown here:

// this works
val objects = Array("a", 1)
val arrayOfObject = objects.asInstanceOf[Array[Object]]
AJavaClass.printObjectArray(arrayOfObject)

See Also

  • A discussion of the Array class, including how it’s a “special kind of collection”
  • Recipe 17.1, “Going to and from Java Collections” discusses how to convert many types of collections when interacting with Java code