繼讀來說說 Scala 為什麼吸引我唄,在這一篇裡面與其他語言比較的部份沒那麼多,大部份是在講 Scala 和 Java 的一些不同,以及一些可以讓你比較省事的功能。

偏好 Immutable 物件,但也給你選擇的自由

由於 Scala 將 Functional Programming (FP) 做為預設偏好的編程典範,所以也提倡所謂的 immutable 物件,至於什麼是 immutable 物件呢?簡單地來說,就是不論你對他做什麼樣的操作,他都永遠不會改變。

其中一個註明的例子是 Java 當中的 String 物件,不論你呼叫 String 裡的哪一個函式,原來的 String 物件是不會被更動的,只是產生了一份新的 String 物件而已。

例如:

public class Test {
    public static void main (String [] args) {
        String str1 = "Hello World";
        String str2 = str1.substring(0, 5);
        System.out.println (str1);  // "Hello World"
        System.out.println (str2);  // "World"
    }
}

同樣的,在 Scala 當中,幾乎大部份預設的資料結構都是 immutable 的,特別明顯的就是 Collection 的相關類別,像是 List、Map 這些東西,如果沒有特別指明的話,所使用的都是 immutable 的版本。

例如:

val xs1 = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val xs2 = xs1.filter(_ % 2 == 0)
println (xs1) // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
println (xs2) // List(2, 4, 6, 8, 10)

至於 immutable 物件的好處,諸如 Thread-safe 等等,其他地方都已經把他講到爛了,所以這邊就不再贅述。

不過要特別提的一點,就是 Scala 給你選擇的自由,他把一整套工具都給你了,所以當你發現真的需要 mutable 物件的 Collection 時候,也沒有問題,只要選用 scala.collection.mutable 裡的東西就可以了,幾乎所有的容器都有一份 immutable 和 mutable 版本,所以你可以選擇適合自己需求的東西。

在 Scala 中的 == 運算子

Java 當中的 == 運算子在不同的狀況下有不同的語意:

  • 當比較的變數是基本資料型態(如 int 或 boolean 這些),比的是『值』
  • 當比較的變數是 Reference,比的是該 Reference 是不是指到同一個物件

一個常常用來示範 Java 中 == 運算子的範例程式如下:

public class Test {
    public static void main (String [] args) {
        int x1 = 123;
        int x2 = 100 + 20 + 3;
        String strPool1 = "Hello World";
        String strPool2 = "Hello World";
        String str1 = new String("Hello World");
        String str2 = new String("Hello World");

        // true,因為值相等,都是 123
        System.out.println (x1 == x2);  

        // true,因為指到 String Pool 裡的同一個物件
        System.out.println (strPool1 == strPool2);      

        // false,雖然值都是 "Hello World",但是是不同物件
        System.out.println (str1 == str2);      
    }
}

相較之下,由於 Scala 是個純物件導向語言,已經沒有基本資料型態和 Reference 資料型態的概念,所以對於 == 的處理也有一點不同--在 Scala 當中,== 用以比較值(在物件有正確實作 hashCode 與 equals 時)。

簡單的來說,在 Scala 當中的 x == y 約相當於在 Java 中呼叫 x.equals(y) 這樣子,這是和 Java 差別滿多的一點。也因為如此,如果真的需要比較兩個變數是不是指到同一份物件,Scala 另外提供了一個叫做 eq 的運算子。

範例如下:

val x1 = 123
val x2 = 100 + 20 + 3
val strPool1 = "Hello World"
val strPool2 = "Hello World"
val str1 = new String("Hello World")
val str2 = new String("Hello World")
val xs1 = List(1, 2, 3)
val xs2 = List(1, 2) + List(3)

println (x1 == x2) // true
println (strPool1 == strPool2) // true, 內容都是 "Hello World"
println (strPool1 eq strPool2) // true, 同一個物件
println (str1 == str2) // true, 內容都是 "Hello World"
println (str1 eq str2) // false, 不同的物件
println (xs1 == xs2) // true, 內容都是 List(1, 2, 3)
println (xs1 eq xs2) // false, 不同的物件

但相較之下,如果你沒有自己實作 equals 這個 method 的話,預設會去比較兩個變數是不是指到相同的物件,算是比較要注意的地方。

class MyData(x: Int)
val data1 = new MyData(3)
val data2 = new MyData(4)
println (data1 == data2) // false,因為沒有實作 equals
println (data1 eq data2) // false,因為是不同的物件

回響