此頁面透過分享每種語言的並列範例,提供 Java 和 Scala 程式語言之間的比較。它適用於了解 Java 並想學習 Scala 的程式設計師,特別是透過了解 Scala 功能與 Java 的比較。
概觀
在進入範例之前,此第一部分提供後續部分的相對簡短介紹和摘要。它以高階方式呈現 Java 和 Scala 之間的相似性和差異,然後介紹您在撰寫程式碼時每天會遇到的差異。
高階相似性
在高階層面上,Scala 與 Java 共享這些相似性
- Scala 程式碼編譯成 *class* 檔案,封裝在 JAR 檔案中,並在 JVM 上執行
- 它是一種 物件導向程式設計 (OOP) 語言
- 它是靜態型別
- 兩種語言都支援 lambda 和 高階函式
- 它們都可以與 IntelliJ IDEA 和 Microsoft VS Code 等 IDE 搭配使用
- 專案可以使用 Gradle、Ant 和 Maven 等建置工具建置
- 它擁有絕佳的函式庫和架構,用於建置伺服器端、網路密集型應用程式,包括網路伺服器應用程式、微服務、機器學習等等(請參閱 “Awesome Scala” 清單)
- Java 和 Scala 都可以使用 Scala 函式庫
- 它們可以使用 Akka actors 函式庫 來建置基於 actor 的並行系統,並使用 Apache Spark 來建置資料密集型應用程式
- 它們可以使用 Play Framework 來開發伺服器端應用程式
- 您可以使用 GraalVM 將專案編譯成原生可執行檔
- Scala 可以無縫使用為 Java 開發的豐富函式庫
高階層面的差異
同樣在高階層面上,Java 和 Scala 之間的差異為
- Scala 具有簡潔但可讀的語法;我們稱之為表達式
- 儘管它是靜態型別,但 Scala 通常感覺像是一種動態語言
- Scala 是一種純粹的 OOP 語言,因此每個物件都是類別的實例,而像
+
和+=
這些看起來像運算子的符號實際上是方法;這表示您可以建立自己的運算子 - 除了是一種純粹的 OOP 語言之外,Scala 也是一種純粹的 FP 語言;事實上,它鼓勵融合 OOP 和 FP,使用函式處理邏輯,使用物件處理模組化
- Scala 有一整套不可變集合,包括
List
、Vector
,以及不可變的Map
和Set
實作 - Scala 中的一切都是一種表達式:建構式例如
if
敘述、for
迴圈、match
表達式,甚至try
/catch
表達式都有傳回值 - Scala 慣用語法預設偏好不可變性:鼓勵您使用不可變 (
final
) 變數和不可變集合 - 慣用的 Scala 程式碼不使用
null
,因此不會發生NullPointerException
- Scala 生態系統在 sbt、Mill 等其他地方還有其他 建置工具
- 除了在 JVM 上執行之外,Scala.js 專案讓您可以將 Scala 用作 JavaScript 的替代品
- Scala Native 這個專案新增了低階建構,讓你可以撰寫「系統」層級的程式碼,而且還能編譯成原生可執行檔
程式設計層級的差異
最後,以下是你每天撰寫程式碼時會看到的差異
- Scala 的語法極為一致
- 變數和參數定義為
val
(不可變,就像 Java 中的final
)或var
(可變) - 類型推論 讓你的程式碼感覺是動態型別的,而且有助於讓你的程式碼簡潔
- 除了簡單的
for
迴圈之外,Scala 還有強大的for
理解,可以根據你的演算法產生結果 - 樣式比對和
match
表達式會改變你撰寫程式碼的方式 - 預設撰寫不可變的程式碼會導致撰寫表達式而非陳述式;隨著時間推移,你會發現撰寫表達式會簡化你的程式碼(以及你的測試)
- 頂層定義 讓你可以在任何地方放置方法、欄位和其他定義,這也能產生簡潔、具表現力的程式碼
- 你可以透過將多個特質「混合」到類別和物件中來建立混合(特質類似於 Java 8 及更新版本中的介面)
- 類別預設為封閉,支援 Joshua Bloch 的Effective Java 慣用語:「設計和文件化繼承,否則禁止繼承」
- Scala 的 脈絡抽象 和詞彙推論 提供了一系列功能
- Scala 有最先進的、第三方、開源函數式程式庫
- Scala case 類別就像 Java 14 中的記錄;它們可協助您在撰寫 FP 程式碼時建模資料,並內建支援模式比對和複製等概念
- 拜依名稱參數、中綴表示法、可選括號、擴充方法和高階函數等功能所賜,您可以建立自己的「控制結構」和 DSL
- Scala 檔案不必根據它們包含的類別或特質來命名
- 許多其他好處:伴隨類別和物件、巨集、聯集和交集、數字文字、多個參數清單、參數的預設值、命名參數等
功能與範例比較
有了這個引言,以下各節將提供 Java 和 Scala 程式語言功能的並排比較。
OOP 風格的類別和方法
本節提供與 OOP 風格類別和方法相關的功能比較。
註解
//
|
//
|
OOP 風格類別、主要建構函式
Scala 沒有遵循 JavaBeans 標準,因此我們在此顯示等同於後續 Scala 程式碼的 Java 程式碼,而不是顯示以 JavaBeans 風格撰寫的 Java 程式碼。
class Person {
|
class Person (
|
輔助建構函式
public class Person {
|
class Person (
|
預設為封閉類別
“計畫繼承,否則禁止繼承。”
final class Person
|
class Person
|
開放擴充的類別
class Person
|
open class Person
|
單行方法
public int add(int a, int b) {
|
def add(a: Int, b: Int): Int = a + b
|
多行方法
public void walkThenRun() {
|
def walkThenRun() =
|
不可變欄位
final int i = 1;
|
val i = 1
|
可變欄位
int i = 1;
|
var i = 1
|
介面、特質和繼承
本節比較 Java 介面和 Scala 特質,包括類別如何擴充介面和特質。
介面/特質
public interface Marker {};
|
trait Marker
|
簡單介面
public interface Adder {
|
trait Adder
|
具有具體方法的介面
public interface Adder {
|
trait Adder
|
繼承
class Dog extends Animal implements HasLegs, HasTail
|
class Dog extends Animal, HasLegs, HasTail
|
擴充多個介面
這些介面和特質具有具體的、已實作的方法(預設方法)
interface Adder {
|
trait Adder
|
Mixins
不適用 |
類別 DavidBanner
|
控制結構
此部分比較 控制結構 在 Java 和 Scala 中的差異。
if
敘述,單行
if (x == 1) { System.out.println(1); }
|
if x == 1 then println(x)
|
if
敘述,多行
if (x == 1) {
|
if x == 1 then
|
if, else if, else
if (x < 0) {
|
if x < 0 then
|
if
作為方法主體
public int min(int a, int b) {
|
def min(a: Int, b: Int): Int =
|
從 if
傳回值
在 Java 中稱為三元運算子
int minVal = (a < b) ? a : b;
|
val minValue = if a < b then a else b
|
while
迴圈
while (i < 3) {
|
while i < 3 do
|
for
迴圈,單行
for (int i: ints) {
|
//建議使用
|
for
迴圈,多行
for (int i: ints) {
|
for
|
for
迴圈,多個產生器
for (int i: ints1) {
|
for
|
帶有守衛 (if
) 表式的產生器
List ints =
|
for
|
for
理解
不適用 |
val list =
|
切換/比對
字串 monthAsString = "";
|
val monthAsString = day 比對
|
切換/比對,每個情況有多個條件
字串 numAsString = "";
|
val numAsString = i 比對
|
try/catch/finally
try {
|
try
|
集合類別
此區段比較 Java 和 Scala 中提供的 集合類別。
不可變集合類別
建立不可變集合實例的範例。
序列
清單字串 = 清單.of("a", "b", "c");
|
val 字串 = 清單("a", "b", "c")
|
集合
設定集合 = 設定.of("a", "b", "c");
|
val 集合 = 設定("a", "b", "c")
|
地圖
地圖地圖 = 地圖.of(
|
val 地圖 = 地圖(
|
可變集合類別
Scala 具有可變集合類別,例如 ArrayBuffer
、Map
和 Set
,位於其 scala.collection.mutable 套件中。在將它們 匯入 當前範圍後,它們的建立方式就像剛剛顯示的不可變 List
、Vector
、Map
和 Set
範例一樣。
Scala 也有 Array
類別,您可以將其視為 Java array
基本型別的包裝器。Scala Array[A]
對應到 Java A[]
,因此您可以將這個 Scala Array[String]
val a = Array("a", "b")
視為由這個 Java String[]
支援
String[] a = {"a", "b"};
但是,Scala Array
也有 Scala 集合中預期的所有函式方法,包括 map
和 filter
val nums = Array(1, 2, 3, 4, 5)
val doubledNums = nums.map(_ * 2)
val filteredNums = nums.filter(_ > 2)
由於 Scala Array
的表示方式與 Java array
相同,因此您可以在 Scala 程式碼中輕鬆使用回傳陣列的 Java 方法。
儘管討論了
Array
,但請記住,在 Scala 中通常有其他替代方案比Array
更適合。陣列對於與其他語言(Java、JavaScript)進行互操作很有用,而且在編寫需要從底層平台中榨取最大效能的低階程式碼時也可能很有用。但一般來說,當你需要使用序列時,Scala 的慣例是偏好使用不可變序列,例如Vector
和List
,然後在真正需要可變序列時才使用ArrayBuffer
。
你也可以使用 Scala CollectionConverters
物件在 Java 和 Scala 集合類別之間進行轉換。在不同的套件中有兩個物件,一個用於從 Java 轉換到 Scala,另一個用於從 Scala 轉換到 Java。此表格顯示可能的轉換
Java | Scala |
---|---|
java.util.Collection | scala.collection.Iterable |
java.util.List | scala.collection.mutable.Buffer |
java.util.Set | scala.collection.mutable.Set |
java.util.Map | scala.collection.mutable.Map |
java.util.concurrent.ConcurrentMap | scala.collection.mutable.ConcurrentMap |
java.util.Dictionary | scala.collection.mutable.Map |
集合類別中的方法
由於能夠將 Java 集合視為串流,因此 Java 和 Scala 現在有許多相同的常見功能方法可用
map
filter
forEach
/foreach
findFirst
/find
reduce
如果你習慣在 Java 中使用 lambda 表達式來使用這些方法,你會發現很容易在 Scala 的 集合類別 上使用相同的方法。
Scala 也有數十種其他 集合方法,包括 head
、tail
、drop
、take
、distinct
、flatten
等許多方法。一開始你可能會疑惑為什麼有這麼多方法,但使用 Scala 之後你會發現,因為這些方法,你幾乎不需要再撰寫自訂 for
迴圈。
(這也表示你幾乎不需要閱讀自訂 for
迴圈。由於開發人員花在閱讀程式碼的時間通常是撰寫程式碼的十倍,因此這點很重要。)
元組
Java 元組的建立方式如下
Pair<String, Integer> pair =
new Pair<String, Integer>("Eleven", 11);
Triplet<String, Integer, Double> triplet =
Triplet.with("Eleven", 11, 11.0);
Quartet<String, Integer, Double,Person> triplet =
Quartet.with("Eleven", 11, 11.0, new Person("Eleven"));
其他 Java 元組名稱為 Quintet、Sextet、Septet、Octet、Ennead、Decade。
任何大小的 Scala 元組都是將值放在括號內建立,如下所示
val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))
列舉
此節比較 Java 和 Scala 中的列舉。
基本列舉
enum Color {
|
enum Color
|
參數化列舉
enum Color {
|
enum Color(val rgb: Int)
|
使用者定義的列舉成員
enum Planet {
|
enum Planet(
|
例外和錯誤處理
本節涵蓋 Java 和 Scala 中例外處理的差異。
Java 使用檢查例外
Java 使用檢查例外,因此在 Java 程式碼中,您以往會寫 try
/catch
/finally
區塊,以及方法上的 throws
子句
public int makeInt(String s)
throws NumberFormatException {
// code here to convert a String to an int
}
Scala 不使用檢查例外
Scala 的慣用做法是不這樣使用檢查例外。當使用可能會擲回例外的程式碼時,您可以使用 try
/catch
/finally
區塊來捕捉擲回例外的程式碼,但後續處理方式不同。
說明這一點的最佳方式是,Scala 程式碼包含回傳值的運算式。因此,您會將程式碼寫成一系列的代數運算式
val a = f(x)
val b = g(a,z)
val c = h(b,y)
這很好,這只是代數。您建立方程式來解決小問題,然後組合方程式來解決更大的問題。
而且非常重要的是,您從代數課程中記得,代數運算式不會短路,它們不會擲回會炸毀一系列方程式的例外。
因此,在 Scala 中,我們的函式不會擲回例外。相反地,它們會回傳 Option
等型別。例如,這個 makeInt
函式會捕捉可能的例外並回傳 Option
值
def makeInt(s: String): Option[Int] =
try
Some(s.toInt)
catch
case e: NumberFormatException => None
Scala Option
類似於 Java Optional
類別。如所示,如果字串轉換為整數成功,Int
會在 Some
值中傳回,如果失敗,會傳回 None
值。Some
和 None
是 Option
的子類型,因此方法宣告傳回 Option[Int]
類型。
當您有 Option
值時,例如 makeInt
傳回的值,有許多方法可以處理它,具體取決於您的需求。此程式碼顯示一種可能的方法
makeInt(aString) match
case Some(i) => println(s"Int i = $i")
case None => println(s"Could not convert $aString to an Int.")
Option
通常用於 Scala,並且內建於標準函式庫中的許多類別中。其他類似的類別組,例如 Try/Success/Failure 和 Either/Left/Right,提供更大的彈性。
有關處理 Scala 中錯誤和例外情況的更多資訊,請參閱 函式錯誤處理 部分。
Scala 獨有的概念
這總結了 Java 和 Scala 語言的比較。
Scala 中有其他概念目前在 Java 11 中沒有對應的概念。這包括