Scala 3 — Book

Interacting with Java

Language

Introduction

This section looks at how to use Java code in Scala, and the opposite, how to use Scala code in Java.

In general, using Java code in Scala is pretty seamless. There are only a few points where you’ll want to use Scala utilities to convert Java concepts to Scala, including:

  • Java collections classes
  • The Java Optional class

Similarly, if you’re writing Java code and want to use Scala concepts, you’ll want to convert Scala collections and the Scala Option class.

These following sections demonstrate the most common conversions you’ll need:

  • How to use Java collections in Scala
  • How to use Java Optional in Scala
  • Extending Java interfaces in Scala
  • How to use Scala collections in Java
  • How to use Scala Option in Java
  • How to use Scala traits in Java
  • How to handle Scala methods that throw exceptions in Java code
  • How to use Scala varargs parameters in Java
  • Create alternate names to use Scala methods in Java

Note that the Java examples in this section assume that you’re using Java 11 or newer.

How to use Java collections in Scala

When you’re writing Scala code and an API either requires or produces a Java collection class (from the java.util package), then it is valid to directly use or create the collection as you would in Java.

However, for idiomatic usage in Scala, such as for loops over the collection, or to apply higher-order functions such as map and filter, you can create a proxy that behaves like a Scala collection.

Here’s an example of how this works. Given this API that returns java.util.List[String]:

public interface Foo {
  static java.util.List<String> getStrings() {
    return List.of("a", "b", "c");
  }
}

You can convert that Java list to a Scala Seq, using the conversion utilities in the Scala scala.jdk.CollectionConverters object:

import scala.jdk.CollectionConverters._
import scala.collection.mutable

def testList() = {
  println("Using a Java List in Scala")
  val javaList: java.util.List[String] = Foo.getStrings()
  val scalaSeq: mutable.Seq[String] = javaList.asScala
  for (s <- scalaSeq) println(s)
}
import scala.jdk.CollectionConverters.*
import scala.collection.mutable

def testList() =
  println("Using a Java List in Scala")
  val javaList: java.util.List[String] = Foo.getStrings()
  val scalaSeq: mutable.Seq[String] = javaList.asScala
  for s <- scalaSeq do println(s)

In the above code javaList.asScala creates a wrapper that adapts a java.util.List to Scala’s mutable.Seq collection.

How to use Java Optional in Scala

When you are interacting with an API that uses the java.util.Optional class in your Scala code, it is fine to construct and use as in Java.

However, for idiomatic usage in Scala, such as use with for, you can convert it to a Scala Option.

To demonstrate this, here’s a Java API that returns an Optional[String] value:

public interface Bar {
  static java.util.Optional<String> optionalString() {
    return Optional.of("hello");
  }
}

First import all members from the scala.jdk.OptionConverters object, and then use the toScala method to convert the Optional value to a Scala Option:

import java.util.Optional
import scala.jdk.OptionConverters._

val javaOptString: Optional[String] = Bar.optionalString
val scalaOptString: Option[String] = javaOptString.toScala
import java.util.Optional
import scala.jdk.OptionConverters.*

val javaOptString: Optional[String] = Bar.optionalString
val scalaOptString: Option[String] = javaOptString.toScala

Extending Java interfaces in Scala

If you need to use Java interfaces in your Scala code, extend them just as though they are Scala traits. For example, given these three Java interfaces:

public interface Animal {
  void speak();
}

public interface Wagging {
  void wag();
}

public interface Running {
  // an implemented method
  default void run() {
    System.out.println("I’m running");
  }
}

you can create a Dog class in Scala just as though you were using traits. Because run has a default implementation, you only need to implement the speak and wag methods:

class Dog extends Animal with Wagging with Running {
  def speak = println("Woof")
  def wag = println("Tail is wagging")
}

def useJavaInterfaceInScala = {
  val d = new Dog()
  d.speak
  d.wag
  d.run
}
class Dog extends Animal, Wagging, Running:
  def speak = println("Woof")
  def wag = println("Tail is wagging")

def useJavaInterfaceInScala =
  val d = Dog()
  d.speak
  d.wag
  d.run

Also notice that in Scala, Java methods defined with empty parameter lists can be called either as in Java, .wag(), or you can choose to not use parentheses .wag.

How to use Scala collections in Java

When you need to use a Scala collection class in your Java code, use the methods of Scala’s scala.jdk.javaapi.CollectionConverters object in your Java code to make the conversions work.

For example, suppose that a Scala API returns a List[String] like this:

object Baz {
  val strings: List[String] = List("a", "b", "c")
}
object Baz:
  val strings: List[String] = List("a", "b", "c")

You can access that Scala List in your Java code like this:

import scala.jdk.javaapi.CollectionConverters;

// access the `strings` method with `Baz.strings()`
scala.collection.immutable.List<String> xs = Baz.strings();

java.util.List<String> listOfStrings = CollectionConverters.asJava(xs);

for (String s: listOfStrings) {
  System.out.println(s);
}

That code can be shortened, but the full steps are shown to demonstrate how the process works. Be sure to notice that while Baz has a field named strings, from Java the field appears as a method, so must be called with parentheses .strings().

How to use Scala Option in Java

When you need to use a Scala Option in your Java code, you can convert the Option to a Java Optional value using the toJava method of the Scala scala.jdk.javaapi.OptionConverters object.

For example, suppose that a Scala API returns an Option[String] like this:

object Qux {
  val optString: Option[String] = Option("hello")
}
object Qux:
  val optString: Option[String] = Option("hello")

Then you can access that Scala Option in your Java code like this:

import java.util.Optional;
import scala.Option;
import scala.jdk.javaapi.OptionConverters;

Option<String> scalaOptString = Qux.optString();
Optional<String> javaOptString = OptionConverters.toJava(scalaOptString);

That code can be shortened, but the full steps are shown to demonstrate how the process works. Be sure to notice that while Qux has a field named optString, from Java the field appears as a method, so must be called with parentheses .optString().

How to use Scala traits in Java

From Java 8 you can use a Scala trait just like a Java interface, even if the trait has implemented methods. For example, given these two Scala traits, one with an implemented method and one with only an interface:

trait ScalaAddTrait {
  def sum(x: Int, y: Int) = x + y     // implemented
}

trait ScalaMultiplyTrait {
  def multiply(x: Int, y: Int): Int   // abstract
}
trait ScalaAddTrait:
  def sum(x: Int, y: Int) = x + y     // implemented

trait ScalaMultiplyTrait:
  def multiply(x: Int, y: Int): Int   // abstract

A Java class can implement both of those interfaces, and define the multiply method:

class JavaMath implements ScalaAddTrait, ScalaMultiplyTrait {
  public int multiply(int a, int b) {
    return a * b;
  }
}

JavaMath jm = new JavaMath();
System.out.println(jm.sum(3,4));        // 7
System.out.println(jm.multiply(3,4));   // 12

How to handle Scala methods that throw exceptions in Java code

When you’re writing Scala code using Scala programming idioms, you’ll never write a method that throws an exception. But if for some reason you have a Scala method that does throw an exception, and you want Java developers to be able to use that method, add the @throws annotation to your Scala method so Java consumers will know the exceptions it can throw.

For example, this Scala exceptionThrower method is annotated to declare that it throws an Exception:

object SExceptionThrower {
  @throws[Exception]
  def exceptionThrower =
    throw new Exception("Idiomatic Scala methods don’t throw exceptions")
}
object SExceptionThrower:
  @throws[Exception]
  def exceptionThrower =
    throw Exception("Idiomatic Scala methods don’t throw exceptions")

As a result, you’ll need to handle the exception in your Java code. For instance, this code won’t compile because I don’t handle the exception:

// won’t compile because the exception isn’t handled
public class ScalaExceptionsInJava {
  public static void main(String[] args) {
    SExceptionThrower.exceptionThrower();
  }
}

The compiler gives this error:

[error] ScalaExceptionsInJava: unreported exception java.lang.Exception;
        must be caught or declared to be thrown
[error] SExceptionThrower.exceptionThrower()

This is good—it’s what you want: the annotation tells the Java compiler that exceptionThrower can throw an exception. Now when you’re writing Java code you must handle the exception with a try block or declare that your Java method throws an exception.

Conversely, if you leave the annotation off of the Scala exceptionThrower method, the Java code will compile. This is probably not what you want, because the Java code may not account for the Scala method throwing the exception.

How to use Scala varargs parameters in Java

When a Scala method has a varargs parameter and you want to use that method in Java, mark the Scala method with the @varargs annotation. For example, the printAll method in this Scala class declares a String* varargs field:

import scala.annotation.varargs

object VarargsPrinter {
  @varargs def printAll(args: String*): Unit = args.foreach(println)
}
import scala.annotation.varargs

object VarargsPrinter:
  @varargs def printAll(args: String*): Unit = args.foreach(println)

Because printAll is declared with the @varargs annotation, it can be called from a Java program with a variable number of parameters, as shown in this example:

public class JVarargs {
  public static void main(String[] args) {
    VarargsPrinter.printAll("Hello", "world");
  }
}

When this code is run, it results in the following output:

Hello
world

Create alternate names to use Scala methods in Java

In Scala you might want to create a method name using a symbolic character:

def +(a: Int, b: Int) = a + b

That method name won’t work well in Java, but what you can do in Scala is provide an “alternate” name for the method with the targetName annotation, which will be the name of the method when used from Java:

import scala.annotation.targetName

object Adder {
  @targetName("add") def +(a: Int, b: Int) = a + b
}
import scala.annotation.targetName

object Adder:
  @targetName("add") def +(a: Int, b: Int) = a + b

Now in your Java code you can use the aliased method name add:

int x = Adder.add(1,1);
System.out.printf("x = %d\n", x);

Contributors to this page: