Как интегрировать Scala-код с Java?

Интеграция Scala с Java — одно из сильных преимуществ экосистемы Scala. Scala полностью компилируется в байт-код JVM, а значит, может использовать существующие Java-библиотеки и классы без обёрток или адаптеров. Также Java может вызывать Scala-код, если соблюдать определённые соглашения.

Использование Java-кода в Scala

Scala может напрямую импортировать и использовать Java-классы, пакеты, интерфейсы и методы, как если бы они были написаны на Scala.

Примеры:

Импорт стандартной библиотеки Java:

import java.util.Date
val now = new Date()
println(now)

Использование сторонних Java-библиотек:

import org.apache.commons.lang3.StringUtils
val result = StringUtils.capitalize("scala")

Вызов методов Java-класса:

// Java class
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
// Scala usage
val calc = new Calculator()
val sum = calc.add(3, 5)

Scala автоматически адаптирует вызов методов, конструкций getX()/setX() и даже принимает boolean как if (condition).

Использование Scala-кода в Java

Для вызова Scala-классов из Java нужно учитывать, что:

  • Scala использует функции как объекты (Function1, Function2, и т.д.)

  • Методы в Scala по умолчанию являются public, но компилируются в методы со специфической сигнатурой

  • Scala-объекты (object) компилируются в final class с суффиксом $ и статическим полем MODULE$

Примеры:

Scala-код:

package demo
object Utils {
def greet(name: String): String = s"Hello, $name"
}
class MathOps {
def square(x: Int): Int = x \* x
}

Java-код:

import demo.MathOps;
import demo.Utils;
public class App {
public static void main(String\[\] args) {
MathOps ops = new MathOps();
System.out.println(ops.square(5));
// доступ к object
System.out.println(Utils$.MODULE$.greet("Java"));
}
}

Альтернативно можно добавить аннотацию @scala.annotation.meta.field или использовать Java-friendly API.

Советы по Java→Scala интеграции

  • Scala автоматически распознаёт геттеры/сеттеры Java в виде полей:
public class User {
private String name;
public String getName() { return name; }
public void setName(String n) { name = n; }
}
val user = new User()
user.name = "John"
println(user.name)
  • Java Optional преобразуется в Scala Option через .asScala из scala.jdk.javaapi.OptionConverters:
import scala.jdk.OptionConverters._
val maybeName: Option\[String\] = someJavaOptional.asScala

Советы по Scala→Java интеграции

  • Используй @BeanProperty для генерации Java-геттеров/сеттеров:
import scala.beans.BeanProperty
class Person {
@BeanProperty var name: String = _
}

Java:

Person p = new Person();
p.setName("John");
System.out.println(p.getName());
  • Используй @SerialVersionUID, если ты сериализуешь классы в Java.

  • Для Java-кода, использующего Scala-коллекции, используй преобразования:

import scala.jdk.CollectionConverters._
val list: java.util.List\[String\] = List("a", "b", "c").asJava

Совместимость коллекций

Между java.util.* и scala.collection.* есть автоматические конвертации:

import scala.jdk.CollectionConverters._
val javaList = new java.util.ArrayList\[String\]()
javaList.add("hello")
val scalaList = javaList.asScala.toList

Также работает и наоборот: scalaList.asJava

Аннотации и дополнительные фичи

  • @throws(classOf[IOException]) позволяет задать проверяемые исключения, чтобы Java-код знал об этом:
@throws(classOf\[IOException\])
def risky(): Unit = throw new IOException("oops")
  • Scala-объекты становятся Singleton в Java, доступные через MODULE$:
MyScalaObject$.MODULE$.method();

Если хочется удобства — можно вручную добавить статическую обёртку в Scala:

object MyUtil {
def add(a: Int, b: Int): Int = a + b
@JvmStatic def addStatic(a: Int, b: Int): Int = add(a, b)
}

Java:

MyUtil.addStatic(3, 4);

Совместимость в sbt

Указание зависимостей на Java:

libraryDependencies += "com.google.guava" % "guava" % "33.0.0-jre"

Поддержка Java 8, 11 и выше указывается через:

javacOptions ++= Seq("--release", "11")

Если используются Java-классы, они должны быть в src/main/java.

Интеграция Scala с Java — двусторонняя и гибкая. Scala понимает Java практически «из коробки», а для Java можно упростить вызовы Scala через аннотации (@BeanProperty, @JvmStatic) и внимание к компиляции объектов (object).