Scala 3 — 书籍

与 Java 交互

语言

简介

本部分介绍如何在 Scala 中使用 Java 代码,以及如何在 Java 中使用 Scala 代码。

通常,在 Scala 中使用 Java 代码非常无缝。只有少数情况下,您需要使用 Scala 实用工具将 Java 概念转换为 Scala,包括

  • Java 集合类
  • Java Optional

同样,如果您正在编写 Java 代码并希望使用 Scala 概念,您需要转换 Scala 集合和 Scala Option 类。

以下部分演示您需要进行的最常见转换

  • 如何在 Scala 中使用 Java 集合
  • 如何在 Scala 中使用 Java Optional
  • 在 Scala 中扩展 Java 接口
  • 如何在 Java 中使用 Scala 集合
  • 如何在 Java 中使用 Scala Option
  • 如何在 Java 中使用 Scala 特征
  • 如何在 Java 代码中处理抛出异常的 Scala 方法
  • 如何在 Java 中使用 Scala 可变参数
  • 创建备用名称以在 Java 中使用 Scala 方法

请注意,本部分中的 Java 示例假定您使用的是 Java 11 或更高版本。

如何在 Scala 中使用 Java 集合

当您编写 Scala 代码,并且 API 要求或生成 Java 集合类(来自 java.util 包)时,直接使用或创建集合是有效的,就像在 Java 中一样。

但是,对于 Scala 中的惯用用法,例如对集合进行 for 循环,或应用高阶函数(例如 mapfilter),您可以创建一个像 Scala 集合一样工作的代理。

以下是如何工作的示例。给定此 API,它返回 java.util.List[String]

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

您可以使用 Scala scala.jdk.CollectionConverters 对象中的转换实用程序将该 Java 列表转换为 Scala Seq

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)

在上述代码中,javaList.asScala 创建一个包装器,将 java.util.List 适配到 Scala 的 mutable.Seq 集合。

如何在 Scala 中使用 Java Optional

当您在 Scala 代码中与使用 java.util.Optional 类的 API 交互时,构造和使用就像在 Java 中一样是可以的。

但是,对于 Scala 中的惯用用法,例如与 for 一起使用,您可以将其转换为 Scala Option

为了演示这一点,这里有一个 Java API,它返回一个 Optional[String]

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

首先从 scala.jdk.OptionConverters 对象导入所有成员,然后使用 toScala 方法将 Optional 值转换为 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

在 Scala 中扩展 Java 接口

如果您需要在 Scala 代码中使用 Java 接口,请像使用 Scala 特征一样对其进行扩展。例如,给定这三个 Java 接口

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");
  }
}

您可以像使用特征一样在 Scala 中创建一个 Dog 类。由于 run 有一个默认实现,因此您只需要实现 speakwag 方法

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

还要注意,在 Scala 中,使用空参数列表定义的 Java 方法可以像在 Java 中一样调用,.wag(),或者您可以选择不使用括号 .wag

如何在 Java 中使用 Scala 集合

当您需要在 Java 代码中使用 Scala 集合类时,请在 Java 代码中使用 Scala 的 scala.jdk.javaapi.CollectionConverters 对象的方法来进行转换。

例如,假设 Scala API 返回一个 List[String],如下所示

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

你可以像这样在 Java 代码中访问 Scala List

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);
}

该代码可以缩短,但显示完整步骤是为了演示该过程的工作原理。请务必注意,虽然 Baz 有一个名为 strings 的字段,但从 Java 来看,该字段显示为一个方法,因此必须使用括号 .strings() 调用。

如何在 Java 中使用 Scala Option

当需要在 Java 代码中使用 Scala Option 时,可以使用 Scala scala.jdk.javaapi.OptionConverters 对象的 toJava 方法将 Option 转换为 Java Optional 值。

例如,假设一个 Scala API 返回一个 Option[String],如下所示

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

然后你可以像这样在 Java 代码中访问 Scala Option

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

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

该代码可以缩短,但显示完整步骤是为了演示该过程的工作原理。请务必注意,虽然 Qux 有一个名为 optString 的字段,但从 Java 来看,该字段显示为一个方法,因此必须使用括号 .optString() 调用。

如何在 Java 中使用 Scala 特征

从 Java 8 开始,你可以像使用 Java 接口一样使用 Scala 特质,即使该特质已实现方法。例如,给定这两个 Scala 特质,一个带有已实现的方法,一个仅带有接口

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

Java 类可以实现这两个接口,并定义 multiply 方法

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

如何在 Java 代码中处理抛出异常的 Scala 方法

使用 Scala 编程惯用法编写 Scala 代码时,你永远不会编写一个抛出异常的方法。但如果由于某种原因,你有一个抛出异常的 Scala 方法,并且希望 Java 开发人员能够使用该方法,请向你的 Scala 方法添加 @throws 注释,以便 Java 使用者知道它可以抛出的异常。

例如,此 Scala exceptionThrower 方法带注释,声明它抛出 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")

因此,你需要在 Java 代码中处理异常。例如,这段代码无法编译,因为我没有处理异常

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

编译器给出此错误

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

这是好的——这就是你想要的:注释告诉 Java 编译器 exceptionThrower 可以抛出异常。现在,当你编写 Java 代码时,你必须使用 try 块处理异常或声明你的 Java 方法抛出异常。

相反,如果你从 Scala exceptionThrower 方法中省略注释,则 Java 代码将编译。这可能不是你想要的,因为 Java 代码可能无法解释 Scala 方法抛出的异常。

如何在 Java 中使用 Scala 可变参数

当 Scala 方法具有 varargs 参数,并且您想在 Java 中使用该方法时,请使用 @varargs 注释标记 Scala 方法。例如,此 Scala 类中的 printAll 方法声明了一个 String* varargs 字段

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)

由于 printAll 使用 @varargs 注释声明,因此可以从具有可变数量参数的 Java 程序中调用它,如本示例所示

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

当运行此代码时,它将产生以下输出

Hello
world

创建备用名称以在 Java 中使用 Scala 方法

在 Scala 中,您可能希望使用符号字符创建方法名称

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

该方法名称在 Java 中不能很好地工作,但您可以在 Scala 中使用 targetName 注释为该方法提供“备用”名称,这将是该方法在 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

现在,您可以在 Java 代码中使用别名方法名称 add

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

此页面的贡献者