Scala 與 Java:我為什麼要學習 Scala?
已發表: 2022-03-11誠然,“Scala 很難”的說法是有道理的,但學習曲線非常值得投資。 該語言的一些更複雜的特性(元組、函數、宏等等)最終使開發人員更容易編寫更好的代碼並通過在 Scala 中編程來提高性能。 坦率地說,我們是程序員,如果我們不夠聰明,無法學習一種具有一定複雜性的語言,那麼我們就做錯了。
Scala 是一種類型安全的 JVM 語言,它將面向對象和函數式編程合併為一種極其簡潔、合乎邏輯且非常強大的語言。 有些人可能會驚訝地發現,Scala 並不像他們想像的那麼新,它於 2003 年首次推出。然而,特別是在過去的幾年裡,Scala 已經開始發展出大量的追隨者。 這就引出了“為什麼選擇 Scala?”的問題。
本文探討了 Scala 的優勢,尤其是與 Java 相比(因為 Scala 是為在 JVM 中運行而編寫的)。 Scala 並不是創建“更好的 Java”的唯一嚐試。 Kotlin 和 Ceylon 等替代品也走上了這條道路,但他們做出了基本決定,即在語法上與 Java 語言本身保持非常接近,以最大限度地減少學習曲線。 這似乎是一個好主意,但它最終有點弄巧成拙,因為它迫使您停留在許多相同的 Java 範式中,而這些範式正是最初想要創建“更好的 Java”的原因。
相比之下,Scala 是專門為成為一種更好的語言而創建的,它擺脫了 Java 中那些它認為限制性、過於乏味或讓開發人員感到沮喪的方面。 因此,確實存在代碼差異和範式轉換,這可能會使早期學習 Scala 編程變得更加困難,但結果是一種更清晰且組織良好的語言,最終更易於使用並提高了生產力。
Scala 與 Java:哪個更複雜?
儘管 Java 語言的簡單性是其成功的一部分,但具有諷刺意味的是,它也導致了其複雜性。 當然,您幾乎可以用 Java 編寫任何東西,但是這樣做所需的代碼行數可能令人生畏。 另一方面,Scala 中的編程結構稍微複雜一些。 但是,如果您可以編寫稍微複雜一點的單行代碼來代替 20 行“更簡單”的 Java 代碼,那麼哪一個更複雜呢?
事實上,Java 通常過於冗長。 在 Scala 中,編譯器非常聰明,因此這避免了開發人員需要明確指定編譯器可以推斷的那些東西。 例如,比較這個簡單的“Hello World!” Java 與 Scala 中的程序:
Java中的Hello World:
public class HelloJava { public static void main(String[] args) { System.out.println("Hello World!"); } }
Scala 中的 Hello World:
object HelloScala { def main(args: Array[String]): Unit = { println("Hello World!") } }
雖然這兩種語言之間沒有很大的區別,但即使在這個簡單的示例中,Scala 也不那麼冗長。
舉一個更實際的例子,讓我們看一下創建一個簡單的字符串列表:
爪哇:
List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3");
斯卡拉:
val list = List("1", "2", "3")
當然,Java 中有一些技巧可以稍微縮短代碼,但在標準用法中沒有。
現在考慮一個例子,我們有一個數字字符串列表,但我們想將該列表轉換為整數列表:
爪哇:
List<Integer> ints = new ArrayList<Integer>(); for (String s : list) { ints.add(Integer.parseInt(s)); }
斯卡拉:
val ints = list.map(s => s.toInt)
由於 Scala 的函數屬性,這種轉換變得非常簡單。
類示例:Java 與 Scala
讓我們更進一步,比較 Java 和 Scala 中的標準 bean/普通舊 Java 對象 (POJO) 創建。
一、Java版本:
public class User { private String name; private List<Order> orders; public User() { orders = new ArrayList<Order>(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Order> getOrders() { return orders; } public void setOrders(List<Order> orders) { this.orders = orders; } } public class Order { private int id; private List<Product> products; public Order() { products = new ArrayList<Product>(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public List<Product> getProducts() { return products; } public void setProducts(List<Product> products) { this.products = products; } } public class Product { private int id; private String category; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } }
呸。 洛塔代碼。

現在是 Scala 版本:
class User { var name: String = _ var orders: List[Order] = Nil } class Order { var id: Int = _ var products: List[Product] = Nil } class Product { var id: Int = _ var category: String = _ }
我們說哪種語言更複雜?!
我們公平嗎?
如果您已經做到了這一點,並且是一名 Java 程序員,那麼您此時可能會認為我對代碼進行了不公平的比較。 畢竟,沒有什麼能阻止我在 Java 中創建公共變量,然後擺脫 getter 和 setter。
但是,如果您回想一下 Java 中 getter 和 setter 背後的推理,它專門用於面向未來的。 也就是說,如果您以後需要為變量的獲取或設置添加一些邏輯,則必須重寫這些公共變量以使用方法(這就是為什麼在 Java 中鼓勵使用 getter 和 setter )。 然而,在 Scala 編程中,情況並非如此。 由於語言設計,抽象保持不變,不需要 getter 和 setter。 例如,考慮一下,如果您嘗試將名稱設置為 null,則 Scala 中的這個修改後的User
類會引發NullPointerException
:
class User { private var _name: String = _ var orders: List[Order] = Nil def name = _name def name_=(name: String) = { if (name == null) { throw new NullPointerException("User.name cannot be null!") } _name = name }
您仍然可以像這樣設置名稱:
user.name = "John Doe"
請注意,這完全消除了預配置方法訪問器的需要。
此外,由於 Scala 更喜歡不變性,我可以在 Scala 中使用案例類更簡潔地編寫此代碼:
case class User(name: String, orders: List[Order]) case class Order(id: Int, products: List[Product]) case class Product(id: Int, category: String)
太瘋狂了,我要寫的代碼少了多少。
進一步舉例
現在考慮具有上述類的場景,我想在User
類中添加一個漂亮的小方法,該方法返回User
訂購的所有Products
的列表:
在 Java 的冗長世界中:
public List<Product> getProducts() { List<Product> products = new ArrayList<Product>(); for (Order order : orders) { products.addAll(order.getProducts()); } return products; }
幸運的是, java.util.List
有一個addAll
方法,或者getProducts()
在 Java 中會更長。
另一方面,在 Scala 中,我們只需要:
def products = orders.flatMap(o => o.products)
您可以看到 Scala 語言實現要小得多。 是的,對於 Scala 新手來說,它可能看起來更複雜,但是一旦你真正完全理解了它背後的概念,Scala 代碼看起來會比 Java 代碼簡單得多。
讓我們在這裡變得更複雜一些。 如果我們只想獲取特定Category
中的Products
怎麼辦?
在這種情況下,我們無法利用java.util.List
中的addAll
方法,因此Java中的事情變得更加醜陋:
public List<Product> getProductsByCategory(String category) { List<Product> products = new ArrayList<Product>(); for (Order order : orders) { for (Product product : order.getProducts()) { if (category.equals(product.getCategory())) { products.add(product); } } } return products; }
然而,在 Scala中,代碼仍然相當簡單。 我們簡單地使用flatMap
將來自每個Order
flattened 的產品列表組合在一個列表中,然後我們過濾以僅包含與類別匹配的產品:
def productsByCategory(category: String) = orders.flatMap(o => o.products).filter(p => p.category == category)
動態與靜態
在過去的幾年裡,當然不乏新的語言,但是最近出現的幾乎所有其他語言都是動態的,而 Scala 是靜態類型的。
作為一名專業的開發人員——儘管我知道並使用許多動態語言——我認為編譯時檢查對於編寫可靠的代碼非常重要。 在動態語言中,您永遠無法確定您的代碼是否足夠無錯誤和健壯,除非您實際在各種場景中運行它。 這可能導致代碼中潛在的嚴重缺陷,這些缺陷在代碼投入生產之前永遠不會被發現。
包起來
希望本文將 Java 與 Scala 進行了充分的對比,足以讓您初步了解 Scala 的強大功能,並激發您學習這門語言的興趣。 它不僅是一種很棒的語言,可以讓編程變得不那麼乏味,更有趣,而且它也被世界上一些最大的公司(LinkedIn、Twitter、FourSquare、The Guardian 等)使用。
Scala 的流行度和使用量正在迅速上升,Scala 開發人員的空缺職位數量不斷增加就證明了這一點。 如果您還沒有這樣做,那麼現在是開始順勢而為並停止問“為什麼要學習 Scala?”的好時機。