Scala不是更好的Java

banq發表於2017-03-05
Scala不是更好的Java,而是一個具有自己的生態系統,最佳實踐和方法的非常獨特的語言。

當人們談論Scala的使用經驗時,經常說可以將Scala看作是更好的Java。許多公司特別是在2008-2009年間採用Scala的公司,並不想放棄Java等熟悉的工具,而只是將Scala整合到基於Maven的現有工作流程中。然而現在已經不再這樣了。在大多數情況下,當代Scala技術棧再也不使用Maven作為構建工具,不使用Spring作為DI容器,很少使用傳統的設計模式。那麼他們使用什麼?

依賴注入

可以說最流行的Scala Web框架是Play,當它剛剛出來時,它沒有提供任何基礎設施以實現DI。2.4版本提供了將Google Guice一起作為一個折中的DI解決方案。不是在Scala世界的每個人都非常渴望重回Java註釋的,幸運的是,這其實也沒有必要。使用Play框架,總是可以用另一個執行時解決方案替換Guice,或者使用一個完全不同的路由並使用所謂的編譯時(compile-time)依賴注入。

使用編譯時(compile-time)DI,能讓服務類接受較低階別的依賴作為建構函式的引數。在Scala中,建構函式引數可以以常規方法訪問,所以大多數時候,這些服務可能是完全無狀態的。這種方法也被幾個Java(和.NET)專家提倡,但它絕不是主流。有趣的是,Scala開發人員比Java / C#使用者有優勢,因為他們可以透過將基於宏的庫與延遲定義組合來實現簡化:

// trait AppComponents
lazy val userDao = wire[UserDao]
lazy val sessionDao = wire[SessionDao]
lazy val userService = wire[UserService]
lazy val authService = wire[AuthService]
<p class="indent">


上面的例子使用了一個來自MacWire庫的函式wire。在編譯階段,此函式將分析每個類並呼叫其建構函式,同時將已初始化的服務作為引數傳遞。此外,使這些值延遲確保編譯器而不是開發人員在將來負責初始化順序。

毫不奇怪,這種方法在近年來變得非常流行,並且使得Play小組考慮將其作為在即將到來的版本的框架中的預設做法。

資料庫訪問

開發的另一個典型方面是與資料庫互動。當Java第一次出來,很高興看到一個查詢資料庫語言能夠開箱即用。然而,使用純JDBC會編寫很多程式碼樣板,因此主流Java方法是使用ORM。

當使用ORM時,通常建立領域類並註釋它們以描述類如何對映到資料庫模式。作為回報,ORM為您提供簡單的方法來持久化和檢索域物件,同時自動執行物件關係對映。這種方法在理論上肯定看起來很高大上,但在實踐中,它導致了許多問題。來自Java社群的一些人指出,手動編寫純SQL實際上要比依靠ORM生成必要的語句更好。最突出的例子是名為jOOQ的庫,它允許開發人員使用Java方法編寫型別安全的SQL程式碼:

create.selectFrom(BOOK)
      .where(BOOK.PUBLISHED_IN.eq(2011))
      .orderBy(BOOK.TITLE)
<p class="indent">

然而,這種方法在Java中並不常見,而這個特定的庫質量不是很高。所以大多數人繼續使用ORM。

有趣的是,ORM從未真正在Scala中大幅度使用。似乎沒有人需要它。用於處理資料庫的最流行的Scala庫是ScalikeJDBC,Slick,Quill。所有這些都更類似於jOOQ而不是Hibernate。同樣,Scala使用者比他們的Java同事更有優勢,因為大多數這些庫嚴重依賴於Java中不存在的Scala特性(宏,隱式引數,字串插值)。

考慮以下使用ScalikeJDBC的示例:


// class UserDao
def getUser(userCode: String): Try[Option[User]] = Try {
  NamedDB('auth).readOnly { implicit session =>
    sql"select * from users where user_code = $userCode".
      map(User.fromRS).headOption().apply()
  }
}
<p class="indent">


程式碼可能看起來好像它容易遭受SQL隱碼攻擊,但實際上它不是:SQL字串在後臺轉換為型別安全PreparedStatement。該getUser方法還使用一些Scala特性 - 即Try和Option- 更好地預期的結果型別。我已經解釋瞭如何使用表示式組合monadic結構使您的程式碼更清晰,但重要的是,Java中沒有人這樣做,因為語言不支援。


JSON序列化

在Java中序列化和解析JSON的最流行的庫是Jackson。Jackson被包括為許多(可能是大多數)與JSON相關庫中的低階依賴,幾乎每個人都廣泛使用。Jackson使用反射API對映欄位,但預設行為可以透過註釋進行調整:

public class Name {
  @JsonProperty("firstName")
  public String _first_name;
}
<p class="indent">


因為Java世界中的一切都圍繞著JavaBeans的概念,預設情況下,Jackson假定你的類包含私有的可變欄位和getter / setter等方法來訪問/改變內部欄位。有趣的是,Jackson還包含一個子專案jackson-module-scala。它絕對也有使用者,但我的經驗表明,大多數Scala開發人員喜歡使用別的東西。

Scala有幾個開發JSON庫,大多數不是基於反射。讓我們來看看最流行的一個 - play-json。

在Scala中,通常不會讓您的資料類可變。因此,你建立一個case class並使其不可變:

case class Tag(id: UUID, text: String)
<p class="indent">


如何將它序列化為JSON?這很簡單!只需新增一個隱式的型別值Writes:

object Tag {
implicit val writes = Json.writes[Tag]
}
就是這樣!你不需要實現特殊的標記介面或在任何地方註冊你的類。這基本上是type classes在起作用,令人驚訝的是,你真的不需要知道背後這個原理,直接簡單使用它們。另一個有趣的事情是,Json.writes輔助方法是基於宏的,而不是基於反射的。再次,Java開發人員不能使用這個庫,因為Java不支援型別類和宏。

結論

Scala開發人員往往不需要使用Spring進行依賴注入、不需要使用Hibernate訪問資料庫以及不需要使用基於反射的庫用於JSON序列化。這些目標實現方式是基於Java中不存在的具有特定Scala特性的解決方案。因此,Scala不是一個更好的Java,而是一個具有自己的生態系統,最佳實踐和方法的非常獨特的語言。如果你遇到一個仍然相信“Scala是更好的Java”神話的人,請傳送他們這篇文章。


The myth of using Scala as a better Java - Applied

相關文章