[comment]: # 學習Scala: 初學者應該了解的知識
Scala開發參照清單
這里列出在開發一個Scala工程中需要參照的資料。
官網網站
文檔網站
http://docs.scala-lang.org/index.html
Cheatsheet
http://docs.scala-lang.org/cheatsheets/
代碼風格
http://docs.scala-lang.org/style/
設計模式
https://wiki.scala-lang.org/display/SYGN/Design+Patterns
Language Specification
http://www.scala-lang.org/files/archive/spec/2.11/
API
http://www.scala-lang.org/api/current/#package
Scala wiki
Scala Tools and Libraries
https://wiki.scala-lang.org/display/SW/Tools+and+Libraries
Scala的初次約
Scala是由Martin Odersky設計的一種結合了函數式編程(不僅僅有這個)的強類型語言。
Scala的代碼會被編譯成Java bytecode,並在JVM上運行的。
可以認為Scala是Java的擴充,一個Java語言的超集。
Scala的設計思想之一,來自於對Java語言的一些缺點的批評。
個人也覺得Java語言發展的太慢,尤其是和C#、.NET相比。
Scala的橫空出世,提供了一個積極性的靈感,解決了Java語言的發展問題,給Java社區帶來強大的活力。
Scala的優勢
-
Java有的優勢,Scala都有
-
Scala的編程風格更簡潔
Scala的簡潔風格,也很可能降低可讀性。所以在團隊開發中,還是需要一個良好的代碼規范來約束。
- 性能很好
和Java的性能一樣,比Python快的太多了。 - 可以直接使用Java類庫。
- Java也可以使用Scala的類庫。
個別情況下,需要在Scala里做特殊的支持。
Scala的用途
-
結合Spark,處理大數據。
這是,目前,Scala的一個主要應用。Spark也是那Scala寫的。 -
Java的腳本語言版
可以直接寫Scala的腳本,也可以在.sh直接使用Scala。 -
代替Java。
scala希望提供一種簡潔的語言。不過作為初學者的我,scala極其容易導致代碼的可讀性比較差。
Java語言還是有其優勢。
Scala的代碼風格
由於Scala結合了函數式編程和面向對象語言特征,從這個方面看,有兩種編程風格可以選用。
- 支持函數式編程的面向對象編程風格
- 函數式編程風格
如果你開發的項目主要用於處理數據,函數式編程風格更適用。
本文也主要針對函數式編程風格。
安裝scala
可以從scala的官方網站上下載。
許多人會選擇Intellij IDEA或者Eclipse作為scala的編輯器。
這里有個使用Visual Studio Code作為編輯器的介紹:
Scala on Visual Studio Code
函數式編程到底意味着什么
Scala的再認識
trait, class,object
開始學習Scala會看到trait這個奇怪的詞,而且可以def object。這里解釋一下:
trait: 類似於Interface,不過可以實現聲明的方法。
class: 就是class.
object: 就是Module,一個靜態類。
Scala的語言特征
除了Java的語言特征外,Scala還提供了一下主要特征。
(這個章節比較深奧,可能不足,需要慢慢地更新)
函數式編程(functional programming)
上面已經說過了。
類型推測(typing inference)
這個特征C#也有。建議大家盡量使用這個特點。也就是說
- 避免定義變量的數據類型
一個好處是類型發生變化的時候,改動的代碼會相對較少。 - 對於函數,要定義輸入和輸出的數據類型。
implicit特性
我把對implicit的認識分為這幾個Levels。
Level 0: 完全忽略implicit的存在
如果,你在你的項目里發現有人使用implicit,可以理直氣壯地批評,這是降低可讀性的傑作。
implicit特性應該避免被使用。implicit很可能給讀代碼的人帶來困惑,屬於反直覺的代碼。
Level 1:簡單的認識implicit conversions
比如:一個函數的參數的Long型,調用時輸入一個Int型的數據,也不會出錯,
其后面就是implicit conversions功勞。
implicit conversions邏輯發現類型Int/Long不匹配,
然后在一個implicit view(可以看成一個函數pool,包含了所有的implicit functions)中找一個輸入為Int,輸出為Long的函數,
最后通過這個函數完成類型的轉換。
Scala自身提供了一些必要的implcite conversion functions.
Level 2: 對implicit的有個基本理解
implicit的使用
使用起來很簡單,直接把implicit關鍵字,加到trait/class/object/def/val之前就可以。
在Scala 2.10版后, implicit可以用在三個地方:
例如:
- implicit functions vs implicit conversions
implicit def int2ordered(x: Int): Ordered[Int] =
new Ordered[Int] { /* .. */ }
- implicit classes
implicit classes是針對Pimp-my-library pattern,在Scala語法上的實現。
這個和C#的extesion methods的用意是一樣的。
比如,你想在Scala的List類上,增加一個函數,而不用去修改Scala的發布包,
在Scala 2.10版以后,就可以通過implicit classes實現,
之前的版本,可以通過Pimp-my-library pattern實現。
在下面這個例子中,如果import Helpers._,類IntWithTimes的所有方法都會作用於Int上。
object Helpers {
implicit class IntWithTimes(x: Int) {
def times[A](f: => A): Unit = {
def loop(current: Int): Unit =
if(current > 0) {
f
loop(current - 1)
}
loop(x)
}
}
}
- implicit values vs implicit parameters
來自官方的一個例子:
object ImplicitTest extends App {
/**
* To show how implicit parameters work,
* we first define monoids for strings and integers.
* The implicit keyword indicates that the corresponding object can be used
* implicitly, within this scope, as a parameter of a function marked implicit.
*/
implicit object StringMonoid extends Monoid[String] {
def add(x: String, y: String): String = x concat y
def unit: String = ""
}
implicit object IntMonoid extends Monoid[Int] {
def add(x: Int, y: Int): Int = x + y
def unit: Int = 0
}
/**
* This method takes a List[A] returns an A which represent the combined
* value of applying the monoid operation successively across the whole list.
* Making the parameter m implicit here means we only have to provide
* the xs parameter at the call site, since if we have a List[A]
* we know what type A actually is and therefore what type Monoid[A] is needed.
* We can then implicitly find whichever val or object in the current scope
* also has that type and use that without needing to specify it explicitly.
*/
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
if (xs.isEmpty) m.unit
else m.add(xs.head, sum(xs.tail))
/**
* Here we call sum twice, with only one parameter each time.
* Since the second parameter of sum, m, is implicit its value is looked up
* in the current scope, based on the type of monoid required in each case,
* meaning both expressions can be fully evaluated.
*/
println(sum(List(1, 2, 3))) // uses IntMonoid implicitly
println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly
}
implicit的功能可以分為兩類:
-
implicit conversions (from implicit functions and implicit classes)
當Scala發現類型不匹配,或者正在調用一個對象不存在的函數時,
Scala compiler就會去implicit function list中找一個匹配的函數。 -
implicit arguments (from implicit values and implicit objects)
在函數上可以定義一個implcit參數,編譯器會在implicit的對象列表中,
找到一個類型匹配的對象,並傳入。
作用域
可以想到,implicit有個作用域。這個作用域,和當前的package,以及import的類,
還有Scala的默認有關。
Level 3: 重申:避免使用implicit
Level 4: 如果要使用implicit
- 好好閱讀關於implicit作用域,編譯器的查找complicit對象的順序等知識。
- 定義implicit的開發規范
- 寫好文檔幫助開發人員和用戶理解。
- 限制其使用的場景
- 你要實現一個類似於虛數這樣的新數據類型。
- ...
Collection
Mutability vs Immutability
可變的變量(Mutable variables)(主要是可變的對象)會引起一些潛在的問題:
- 變化后,可能在map中找不到了
- 性能損失(在多線程下,讀寫需要加鎖)
編程方面的建議是:
- 如果可能,使用和開發 immutable 類。
雜七雜八
_ 的用途
Null, null, Nil, Nothing, None, and Unit
-
Null是Scala中的一個Trait.
-
null是一個Null的實例,相當於Java中的null。在面向函數編程中,不要使用,用None代替。
-
None是一個None.type的實例,代替null。
在一個函數中,用於表達這個函數返回了沒有值。可以避免 null pointer exception。 -
Unit是Scala中的一個類型,用於表示一個函數沒有返回值。
有點像Java中的void,不過其實返回了'()'。
// There is no '=' before '{', so the function return Unit.
def funUnit(x: Int) {
x * x
}
// There is a '=' before '{',
// so the function returns the value of the last expression.
def funReturnLastStatement(x: Int) = {
x * x
x + x
}
// Recommend to use explicit return type, and = forever.
def funReturnLastStatementGood(x: Int) : Int = {
x * x
x + x
}
- Nil 是一個空的List實例.
- Nothing是Scala中的一個Trait。基本上沒有用。(我猜的)
## 和 == 對 equals 和 hashCode
在Scala中, ##方法相當於Java中的hashCode方法。
方法相當於Java中的equals方法。
建議使用##和,因為Scala針對value類型實現額外的功能。
Generic classes/functions: +T, -T, <:, >:, <&
- +T: 作用於class, 用於協變(covariant)
- -T:作用於class, 用於逆變(contravariant)
+T, -T 作用於泛型對象之間賦值。
- T <: A: 作用於 function, 約束:T是A的子類,也稱作upper type bound,這是一個協變。
一般會和-T合用。 - T >: A: 作用於 function, 約束:T是A的超類,也稱作lower type bound,這是一個逆變。
一般會和+T合用。 - T <& A: 作用於 function, 約束:存在一個T => A的implicit conversion,也稱作view bound.
<:, >:, <& 作用於泛型函數之間賦值。
請看不變(Invariant), 協變(Covarinat), 逆變(Contravariant) : 一個程序猿進化的故事
編譯.scala文件到jar文件
scalac -d test.jar D:\project\*
參照
- Scala in Depth by ScalaJoshua D. Suereth
- Community-driven documentation for Scala
- Scala design patterns
- Scala guide and cheatsheet
- Scala (programming language)
- Lambda calculus
- Null, null, Nil, Nothing, None, and Unit in Scala