Practical Scala/Java interoperability by small examples

September 14, 2010

If you try to combine Java code with Scala in your application, everything seems to go fine until you bump into big differences between Java collections and Scala collections. A Scala Traversable is not Java Iterable and vice versa; maps are different, etc. So, in practice, you have to do conversion.

Fortunately, there are pretty good implicit converters in Scala 2.8; what remains is to use them properly.

I’ll show a couple of pretty self-explaining examples here.

Here our Scala code returns a Set to Java:

package com.kaching.user

import java.util.{Set => JavaSet}
import scala.collection.JavaConversions._

class GetAllModels(withHistory: Boolean) extends ModelQuery[JavaSet[ModelView]] {

  overide def process = allModels map { m => m view withHistory }
}

process is declared in (a Java interface) as returning JavaSet; so an implicit conversion is applied to the set produced by mapping allModels using m => m.view(withHistory) transformation.

Here’s how a unittest may look like:

    final Model model = new Model("test model");
    GetAllModels query = new GetAllModels(true);
    Mockery mockery = new Mockery();
    final ModelRepository repo = mockery.mock(ModelRepository.class);
    query.setRepo(repo);

    mockery.checking(new Expectations() {{
      one(repo).allModels(); will(returnValue(Sets.newHashSet(model)));
    }});

    Set result = query.process();
    assertEquals(1, result.size());
    assertEquals("test model", result.iterator().next().getName());

In the following example, the Scala class takes a Java Iterable and returns a Java

...
import scala.collection.mutable.{Set => MutableSet}
import java.util.{Set => JavaSet}
import scala.collection.JavaConversions._

class GetModels(val ids: java.lang.Iterable[Id[Model]]) extends ModelQuery[JavaSet[ModelView]] {

  def models: MutableSet[Model] = repo.find(ModelRepository.Key.ID, ids)
  override def process = models map { m => m view false }
}

What happens here: we explicitly show that we accept a Java Iterable but all other conversions are implicit.
First, repo.find(ModelRepository.Key.ID, ids) is a Java code; it returns a Java Set. This set is implicitly transformed into scala.collection.mutable.Set, known in the code under an alias of MutableSet, for clarity.
Then process transform the set into a set of views, same as in the previous example – and converts the result, implicitly, to java.util.Set, just because such is the signature of the method process.

A Java programmer would probably complain about not having the result of process explicitly defined right here. Well… the language is Scala, not Java. Wind of change.

In an unfortunate case when we need both Scala and Java collections, we will have to declare two methods, like in the example below:

import java.util.{Map => JavaMap}
import scala.collection.JavaConversions._

class CompanyIdentity {...
  def allProperties: Map[String, String] = 
    Map("companyShortName" -> companyNameShort,
        "companyLongName"  -> companyNameLong,
        "logoFilename"     -> logoFilename,
        "emailSuffix"      -> emailSuffix,
        "domainName"       -> domainName)

  def allPropertiesForJava: JavaMap[String, String] = allProperties

This may be a little bit awkward, but still writing code in Java is a temporary nuisance, right?