本文由 KnewHow 發表在 ScalaCool 團隊博客。javascript
去年我加入水滴團隊,面試中,面試官問:「你瞭解 Scala 嗎?」java
「不瞭解(尷尬)。」面試
「你知道 Spark 嗎?它就是使用 Scala 編寫的,不過在咱們團隊中,Scala 主要做爲後端語言,咱們 90% 以上的業務代碼都是使用 Scala 編寫。Scala 在國內使用的比較少,可是在國外用的仍是蠻多的,如 Twitter 就是使用 Scala 寫的後端。」編程
自那之後,我便開始了 Scala 的學習之旅。後端
Scala 是由德國的計算機科學家和編程方法教授 Martin Odersky 設計出來的,它的設計原理嚴格遵循數學的邏輯推理。所以它是一門優秀的編程語言,它不只僅在工業界被普遍使用,在學術界也佔用很高的研究地位。bash
因爲以前的 Java 背景,我常常拿 Scala 與 Java 這兩門語言比較。 Scala 和 Java 都基於 JVM,所以 Java 的類庫,Scala 均可以直接使用。可是我對 Scala 印象最深的點,並非「面向對象」,而是它還擁抱「函數式」,尤爲是它的「高階」。編程語言
若是咱們把「面向對象」比做站在地面上觀察事物的原理,而且使用這些原理解決問題,那麼「高階」就是讓你站在山上去看待事物,對問題進行更高層次的抽象。函數
所以不論是解決實際問題,仍是提升對編程語言的認知,Scala 都是一們值得學習的語言。學習
我是從《快學 Scala》這本書開始學習 Scala 的,受此書啓發,我想能不能書寫一個「從 Java 到 Scala 系列」,尋找一棵從 Java 通往 Scala 的連續的知識樹,經過對知識樹的講解,來學習 Scala。this
好了,這就是本系列的第一篇,那麼咱們如何談起呢?
既然 Java 和 Scala 都是「面向對象」的,那咱們就來探索一下什麼是「面向對象」吧。
「模板」是在代碼層面描述一類對象的「行爲」或者「狀態」的代碼,它是抽象的。如 Java 中的類,C 語言中的結構體,它們都是「模板」。
「對象」是在運行期間經過模板在內存中生成的一個個實體,它是具體的。如 Java 在運行期間經過 new 在內存中產生的實體就叫作「對象」。
若是你說共享單車,那麼它就是一個「模板」;若是你說這輛共享單車和那輛共享單車,那它們就是「對象」。
在代碼層面,「對象」的行爲能夠定義爲「方法」,「對象」的狀態能夠定義爲「屬性」,那咱們如何去描述一類「對象」的方法或者屬性呢?-封裝。
例如共享單車,它有車輪,二維碼等屬性,有開鎖和關鎖等行爲。那麼咱們能夠有三種方式來封裝共享單車。
這種方式就是直接封裝,最典型的例子就是 C 語言中的結構體。 封裝共享單車的「模板」以下:
struct SharedBicycle{
車輪;
二維碼;
開鎖;
關鎖;
};
複製代碼
大多數「面向對象」的語言,如 Java,Scala,C++等,都使用這種方式封裝,「模板」以下:
class SharedBicycle{
屬性:車輪;
屬性:二維碼;
方法:開鎖;
方法:關鎖;
}
複製代碼
JavaScript 就是使用這種封裝方式,「模板」以下:
function SharedBicycle(){
this.車輪 = xxx;
this.二維碼 = xxx;
}
//添加原型方法
SharedBicycle.prototype.開鎖 = function(){...};
SharedBicycle.prototype.關鎖 = function(){...};
複製代碼
咱們已經得知,能夠用多種實現面向對象的不一樣技術,那麼什麼是純面向對象的語言呢?
咱們知道 Java 是一門「面向對象」的語言,那麼在 Java 中是否真的「萬物皆對象」?
在 Java 中,咱們能夠寫這麼一段代碼 int a = 3;
而後咱們發現 a
並無封裝任何的屬性或者方法。
所以咱們能夠說 a
不是一個「對象」,Java 不是一門「純粹面向對象」的語言。
再看看 Scala ,不管是低階的 Int
,Double
,仍是高階類型,都封裝有屬性或者方法,所以 Scala 纔是一門「純粹面向對象」的語言。
那麼是什麼支持 Scala 一切皆爲「對象」的呢?-Scala 的通用類型系統。
咱們知道,在 Java 中,全部「對象」的「頂類型」都是 java.lang.Object
,可是 Java 卻忽略了 int
,double
等 JVM 「原始類型」,它們並無繼承 java.lang.Object
。
可是在 Scala 中,存在一個通用的「頂類型」- Any。
Scala 引入了Any
做爲全部類型共同的頂類型。Any
是 AnyRef
和 AnyVal
的超類。
AnyRef
面向 Java(JVM)的對象世界,它對應 java.lang.Object
,是全部對象的超類。
AnyVal
則表明了 Java 的值世界,例如 int
以及其它 JVM 原始類型。
正是依賴這種繼承設計,咱們纔可以使用 Any
定義方法,同時兼容 scala.int
以及 java.lang.String
的實例。
class Person
val allThings = ArrayBuffer[Any]()
val myInt = 42 // Int, kept as low-level `int` during runtime
allThings += myInt // Int (extends AnyVal)
allThings += new Person() // Person (extends AnyRef), no magic here
複製代碼
正是經過這種「通用類型系統」的設計,使得 Scala 擺脫「原始類型」這種邊緣狀況的糾纏,從而實現「純粹的面向對象」。
說完了「頂類型」,咱們再來看看「底類型」。
咱們知道在 Java 中比較鬧心的就是異常處理,當咱們調用一個拋出異常的方法,咱們必須拋出或者處理異常。
可是在 Scala 中,咱們知道一切表達式皆有類型,難道「拋異常」也是有類型的?
scala> val a = Try(throw new Exception("123"))
a: scala.util.Try[Nothing] = Failure(java.lang.Exception: 123)
複製代碼
咱們發現「拋異常」居然是 Nothing
類型,在 Scala 中,難道 Nothing
僅僅是做爲「拋異常」的類型?
scala> def fun(flag:Boolean)={
if(flag){
1 // Int
}else{
throw new Exception("123") //Nothing
}
}
fun: (flag: Boolean)Int
複製代碼
咱們發現 fun
函數並無報錯,並且返回值類型居然是 Int
,這讓咱們有一個大膽的猜想:Nothing
是 Int
的子類型。
[Int] -> ... -> AnyVal -> Any
Nothing -> [Int] -> ... -> AnyVal -> Any
複製代碼
其實在 Scala 中, Nothing
不只僅是 Int
的子類型,它更是全部類型的子類型。 這讓咱們又產生了一個大膽的猜想:難道 Nothing
繼承了全部的類型?咳咳,這個問題咱們之後在討論。
在 Scala 中,還有一個類型 Null
遵循着和 Nothing
同樣的原理。
scala> def fun2(flag:Boolean)={
if(flag){
"123" //String
}else{
null //Null
}
}
fun2: (flag: Boolean)String
複製代碼
同理,咱們能夠得出 Null
是 String的子類型
[String] -> AnyRef -> Any
Null -> [String] -> AnyRef -> Any
複製代碼
那咱們看看 Null
是否能夠兼容 Int
。
scala> def fun3(flag:Boolean)={
if(flag){
123 //Int
}else{
null //Null
}
}
fun3: (flag: Boolean)Any
複製代碼
咱們發現 fun3
的返回值類型居然是 Any
,說明 Null
不能兼容 Scala 的「值類型」,其實從 Scala 的幫助手冊中咱們就能夠得出結論:Null
是全部引用類型的子類型
abstract final class Null extends AnyRef
複製代碼
正因如此,fun3
的返回值類型纔是 Any
,由於 Any
纔是 AnyVal
和 AnyRef
公共的超類。
本文以面向對象爲引子,找到了一個 Java 和 Scala 共有的知識節點,從而引出 Scala 的通用類型系統。那麼在下一篇文章中,咱們由此展開進一步思考,到底什麼是所謂的「類型」,以及 Scala 在類型方面存在哪些與 Java 不一樣的有趣的地方。