Chapter 3
Java Compatibility

Right from the start, the creators of Scala took the Java compatibility issue very seriously. This makes a lot of sense, given all of the already existent Java libraries that are out there.

This chapter starts by showing you how to convert Java collections to Scala and vice versa. It covers how Scala traits relate to Java interfaces, and details how both things can cooperate. Java enums are then mapped into the Scala world. You'll see that, in Scala, you have more than one alternative.

SCALA AND JAVA COLLECTIONS

Collections are probably one of the more used APIs, both in Scala and Java. When interoperating with a Java library it's important to know how you can go from a Java collection to a Scala one and back again.

In the scala.collection package, there are two objects that can be used for this purpose, namely JavaConversions and JavaConverters.

The former provides a bunch of implicit conversions supporting interoperability between Scala and Java collections. For example, Java's List does not have a map method, but you can still call map on it thanks to the implicit conversion into ArrayBuffer:

import java.util.{ArrayList => JArrayList, List => JList}

import scala.collection.JavaConversions._
import scala.collection.mutable

val javaList: JList[Int] = new JArrayList()
javaList.add(42)
javaList.add(1)

val asScala = javaList.map(_ + 1)
println(s"Java List to Scala: $asScala")

The output is as follows:

Java List to Scala: ArrayBuffer(43, 2)

JavaConversions provide two-way implicit conversions among the following types:

scala.collection.Iterable <=> java.lang.Iterable
scala.collection.Iterable <=> java.util.Collection
scala.collection.Iterator <=> java.util.{ Iterator, Enumeration }
scala.collection.mutable.Buffer <=> java.util.List
scala.collection.mutable.Set <=> java.util.Set
scala.collection.mutable.Map <=> java.util.{ Map, Dictionary }
scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap

There are also the following one-way implicit conversions:

scala.collection.Seq => java.util.List
scala.collection.mutable.Seq => java.util.List
scala.collection.Set => java.util.Set
scala.collection.Map => java.util.Map
java.util.Properties => scala.collection.mutable.Map[String, String]

JavaConverters, on the other hand, extends the Java and Scala collections by adding to them the asScala and asJava methods, respectively. It is newer than JavaConversions and makes the conversions explicit.

As a rule of thumb, always remember that explicit is better than implicit. For this reason prefer JavaConverters to JavaConversions, unless you have a very good reason not to.

Table 3.1 shows the available conversions from Scala to Java collections, while Table 3.2 shows the other way around. Note that in both tables s.c stands for scala.collection, while j.u stands for java.util.

Table 3.1 From Scala to Java

Type Method Returned Type
s.c.Iterator asJava j.u.Iterator
s.c.Iterator asJavaEnumeration j.u.Enumeration
s.c.Iterable asJava java.lang.Iterable
s.c.Iterable asJavaCollection j.u.Collection
s.c.mutable.Buffer asJava j.u.List
s.c.mutable.Seq asJava j.u.List
s.c.Seq asJava j.u.List
s.c.mutable.Set asJava j.u.Set
s.c.Set asJava j.u.Set
s.c.mutable.Map asJava j.u.Map
s.c.Map asJava j.u.Map
s.c.mutable.Map asJavaDictionary j.u.Dictionary
s.c.mutable.ConcurrentMap asJavaConcurrentMap j.u.concurrent.ConcurrentMap

Table 3.2 From Java to Scala

Type Method Returned Type
j.u.Iterator asScala s.c.Iterator
j.u.Enumeration asScala s.c.Iterator
java.lang.Iterable asScala s.c.Iterable
j.u.Collection asScala s.c.Iterable
j.u.List asScala s.c.mutable.Buffer
j.u.Set asScala s.c.mutable.Set
j.u.Map asScala s.c.mutable.Map
j.u.concurrent.ConcurrentMap asScala s.c.mutable.ConcurrentMap
j.u.Dictionary asScala s.c.mutable.Map
j.u.Properties asScala s.c.mutable.Map[String, String]

You can easily rewrite the previous example using JavaConverters:

import scala.collection.JavaConverters._
import java.util.{ArrayList => JArrayList, List => JList}

val javaList: JList[Int] = new JArrayList()
javaList.add(42)
javaList.add(1)

val asScala = javaList.asScala.toList.map(_ + 1)
println(s"Java List to Scala: $asScala")

Here you import JavaConverters instead of JavaConversions, and explicitly convert the Java List to Scala using the asScala method.

Note also that, as shown in Table 3.2, calling asScala on a Java List you get back a mutable Buffer. Since we love living in an immutable world when it comes to functional programming, we converted the Buffer into an immutable Scala List by calling toList on it.

Finally, this is the output:

Java List to Scala: List(43, 2)

INTERFACES AND TRAITS

Interoperability problems in collections are what makes Java libraries hard to use in Scala code. But what about general things like classes, interfaces, and traits? Given that Scala's collection of ­language features is a superset of Java's ones, the use of Java libraries in your Scala projects is easy and straightforward. Classes and abstract classes are already present in Scala, and an interface is just a trait without any method implementations. Here is how it works:

// Java interface
public interface CompatibilityInterface {
    public void print();
}

// Scala implementation
class ScalaClass extends CompatibilityInterface{
  override def print() = println("lorem ipsum")
}

(new ScalaClass).print()


// Java class
public class CompatibilityClass {
    public void print(){
        System.out.println("lorem ipsum");
    }
}

// Scala implementation
class ScalaClass extends CompatibilityClass

(new ScalaClass).print()

If you need to use the Java library in your project, you shouldn't have any problems at all. But if you need to export your Scala code so that it can be used in Java, there may be some complications. For instance, the following example won't compile:

// Scala trait
trait ScalaTrait {
  def print() = println("lorem ipsum")
}

// Java class
public class JavaUseOfTrait implements ScalaTrait {}

// Java application
public class JavaApplication {
    public static void main(String[] args) {
        JavaUseOfTrait j = new JavaUseOfTrait();
        j.print(); // This will not work!
    }
}

To be more precise, it will throw the following exception: Error: com.professionalscala.ch3.java.JavaUseOfTrait is not abstract and does not override abstract method print() in com.professionalscala.ch3.ScalaTrait. Java can't find the implementation that is provided in the trait. To understand why it is the case, you must open the folder with the generated byte-code (for example “project/target/scala-2.11/classes/com/professionalscala/ch3”) and see how Java sees the compiled code:

ch3 $: javap ScalaTrait
public interface com.professionalscala.ch3.ScalaTrait {
  public abstract void print();
}

As you may note, for Java ScalaTrait is only an interface without any implementation. This is the case because Scala's byte-code should be compatible with Java 6, but this version does not have any method implementations in the interface (by contrast to Java 8). So these methods should be stored somewhere else. You may have already noticed the compiled .class file named ScalaTrait$class.class, let's see what it contains:

ch3 $: javap ScalaTrait$class
public abstract class com.professionalscala.ch3.ScalaTrait$class {
  public static void print(com.professionalscala.ch3.ScalaTrait);
}

Here's where the implementation is located, but notice that the method is static and it accepts an instance of ScalaTrait. How can you use it in your Java code? Here is where it becomes somewhat complicated:

public class JavaUseOfTrait implements ScalaTrait {
    @Override
    public void print(){
        ScalaTrait$class.print(this);
    }
}

You should implement the generated interface and use Trait's implementation in the overridden method. This is how a Scala class that extends ScalaTrait looks like when decompiled by javap.

Now let's talk about static methods. Scala's object methods are plainly transformed into static ones:

class ScalaObject {
  def say() = println("Hello")
}

object ScalaObject{
  def print() = println("World!")
}

Decompilation yields the following result:

ch3 $: javap ScalaObject
public class com.professionalscala.ch3.ScalaObject {
  public static void print();
  public void say();
  public com.professionalscala.ch3.ScalaObject();
}

As you see, the resulting class contains methods from both Scala's class and its companion object.

To wrap up, any Java library should work in Scala out of the box. Other than type casting, as in the case of collections, there is nothing else that is problematic. The usage of Scala's traits in Java, however, needs some knowledge about how the trait is compiled to the byte-code. If you need your library to be usable on both platforms, you should consider this specific case and create some adapters for Java developers.

SCALA/JAVA ENUMERATIONS

Unfortunately, there is not a direct translation from Java to Scala in terms of Enumeration. You can split your project to include enumeration using Java enumerations by having Java files just for using Java enumeration. However, if you can handle a slight paradigm shift in how to utilize enumerations, you can enact one of two Scala-based alternatives. The first is an example of using an object to an encapsulated number of objects that extend a sealed trait.

sealed trait Color { val hex }

object Color {
  case object Red extends Color { val hex = "#FF0000" }

  case object Yellow extends Color{ val hex = "#ffff00" }

  case object Green extends Color{ val hex = "#00ff00" }
}

While this implementation doesn't give you the ability to iterate over the inner objects (although you can add that either through a macro or list addendum), you get the immediate benefit of being able to use case matches. Another positive is the ability to create even more custom field values for the enumerated type, which can be quite handy depending on the enumeration you are attempting.

To try out the other method of creating enumerations in Scala, you only need to create an object and extend the scala.Enumeration abstract class.

object Colors extends Enumeration {
  val Red, Yellow, Green = Value
}

Note how we left off the hex implementation, because that would require the creation of an abstract class to accommodate that lost functionality. Even if you had added that additional functionality, you would still lose the ability to do a non-exhaustive search, which makes pattern matching less ideal. That said, you inherently maintain the ability to iterate over those values. This can be helpful, and you'll note that the actual implementation is much simpler to understand.

SUMMARY

This chapter examined collections, which are used in APIs in both Scala and Java. You also examined the available conversions between Scala and Java collections. Given that Scala's collection of features is a superset of Java's features, it helps make it more straightforward when using Java libraries in your Scala projects. Unfortunately, there is not a direct translation from Java for Scala in terms of enumerations, but there are Scala-based alternatives that were covered. These are all examples that illustrate how the creators of Scala took the Java compatibility issue very seriously.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset