Passing arrays back and forth between Scala and Java

Scala FAQ: How can I pass an array back and forth between Scala and Java code?

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 in the Scala Cookbook, “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”: http://www.scala-lang.org/docu/files/collections-api/collections_38.html
  • Recipe 17.1 in the Scala Cookbook, “Going to and from Java Collections,” discusses how to convert many types of collections when interacting with Java code

The Scala Cookbook

This tutorial is sponsored by the Scala Cookbook, which I wrote, and was published by O’Reilly in late 2013:

You can find the Scala Cookbook at these locations:

I hope it has been helpful. All the best, Al.