簡介
本節探討如何在 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
迴圈,或套用高階函式,例如 map
和 filter
,你可以建立一個代理,其行為就像 Scala 集合。
以下是如何運作的範例。針對會傳回 java.util.List[String]
的 API
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
。
為了示範,以下是傳回 Optional[String]
值的 Java API
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
具有預設實作,因此您只需要實作 speak
和 wag
方法
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 開始,您可以使用 Scala 特質就像 Java 介面一樣,即使該特質已實作方法。例如,給定這兩個 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 開發人員能夠使用該方法,請將 @throws
註解新增到您的 Scala 方法,這樣 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);