對(duì)于學(xué)習(xí) Scala 的 Java? 開發(fā)人員來說找前,對(duì)象是一個(gè)比較自然、簡(jiǎn)單的入口點(diǎn)判族。在 本系列 前幾期文章中躺盛,我介紹了 Scala 中一些面向?qū)ο蟮木幊谭椒ǎ@些方法實(shí)際上與 Java 編程的區(qū)別不是很大形帮。我還向您展示了 Scala 如何重新應(yīng)用傳統(tǒng)的面向?qū)ο蟾拍畈郾梗业狡淙秉c(diǎn),并根據(jù) 21 世紀(jì)的新需求重新加以改造辩撑。Scala 一直隱藏的一些重要內(nèi)容將要現(xiàn)身:Scala 也是一種函數(shù)語(yǔ)言(這里的函數(shù)性是與其他 dys 函數(shù)語(yǔ)言相對(duì)而言的)界斜。
Scala 的面向函數(shù)性非常值得探討,這不僅是因?yàn)橐呀?jīng)研究完了對(duì)象內(nèi)容合冀。Scala 中的函數(shù)編程將提供一些新的設(shè)計(jì)結(jié)構(gòu)和理念以及一些內(nèi)置構(gòu)造各薇,它們使某些場(chǎng)景(例如并發(fā)性)的編程變得非常簡(jiǎn)單。
C# 2.0 可變?yōu)?null 值的類型其他語(yǔ)言已試圖通過各種方法解決 “可 null 值化” 問題:C++ 一直都忽略了這個(gè)問題,直至最后確定 null 和 0 是不同的值峭判。Java 語(yǔ)言仍然沒有徹底解決這個(gè)問題开缎,而是依賴于自動(dòng)裝箱(autobox)— 將原語(yǔ)類型自動(dòng)轉(zhuǎn)換為它們的包裝器對(duì)象(在 1.1 以后引入)— 幫助 Java 程序員解決問題。一些模式愛好者建議每種類型都應(yīng)該有一個(gè)對(duì)應(yīng)的 “Null Object”林螃,即將自己的所有方法重寫為不執(zhí)行任何操作的類型(實(shí)際上是子類型)的實(shí)例 — 實(shí)踐證明這需要大量工作奕删。C# 1.0 發(fā)布后,C# 設(shè)計(jì)者決定采取一種完全不同的方法解決 null 值化問題疗认。
C# 2.0 引入了可變?yōu)?null 值的類型 的概念完残,重要的是添加了語(yǔ)法支持,認(rèn)為任何特定值類型(基本指原語(yǔ)類型)都可以通過將 null 封裝到一個(gè)泛型/模板類 Nullable<T>横漏,從而提供 null 支持谨设。Nullable<T> 本身是在類型聲明中通過 ? 修飾符號(hào)引入。因此缎浇,int? 表示一個(gè)整數(shù)也可能為 null铝宵。
表面上看,這似乎很合理华畏,但是事情很快就變得復(fù)雜起來鹏秋。int 和 int? 是否應(yīng)該被視為可兼容類型,如果是的話亡笑,什么時(shí)候?qū)?int 提升為 int?侣夷,反之呢?當(dāng)將 int 添加到 int? 會(huì)發(fā)生什么仑乌,結(jié)果會(huì)是 null 嗎百拓?這類問題等等。隨后類型系統(tǒng)進(jìn)行了一些重要的調(diào)整晰甚,可變?yōu)?null 值的類型隨后包含到了 2.0 中 — 而 C# 程序員幾乎完全忽略了它們衙传。
回顧一下 Option 類型的函數(shù)方法,它使 Option[T] 和 Int 之間的界限變得很清晰厕九,看上去要比其他方法更加簡(jiǎn)單蓖捶。在那些圍繞可變?yōu)?null 值類型的反直覺(counterintuitive)提升規(guī)則之間進(jìn)行比較時(shí),尤其如此扁远。(函數(shù)領(lǐng)域?qū)υ搯栴}近二十年的思考是值得的)俊鱼。要使用 Option[T] 必須付出一些努力,但是總的來說畅买,它產(chǎn)生了更清晰的代碼和期望并闲。
.本月,您將首次進(jìn)入 Scala 的函數(shù)編程領(lǐng)域谷羞,查看大多數(shù)函數(shù)語(yǔ)言中常見的四種類型:列表(list)帝火、元組(tuple)、集合(set)和 Option 類型。您還將了解 Scala 的數(shù)組犀填,后者對(duì)其他函數(shù)語(yǔ)言來說十分新鮮萌京。 這些類型都提出了編寫代碼的新方式。當(dāng)結(jié)合傳統(tǒng)面向?qū)ο筇匦詴r(shí)宏浩,可以生成十分簡(jiǎn)潔的結(jié)果。
使用 Option(s)
在什么情況下靠瞎,“無” 并不代表 “什么也沒有”比庄?當(dāng)它為 0 的時(shí)候,與 null 有什么關(guān)系乏盐。
對(duì)于我們大多數(shù)人都非常熟悉的概念佳窑,要在軟件中表示為 “無” 是一件十分困難的事。例如父能,看看 C++ 社區(qū)中圍繞 NULL 和 0 進(jìn)行的激烈討論神凑,或是 SQL 社區(qū)圍繞 NULL 列值展開的爭(zhēng)論,便可知曉一二何吝。 NULL 或 null 對(duì)于大多數(shù)程序員來說都表示 “無”溉委,但是這在 Java 語(yǔ)言中引出了一些特殊問題。
考慮一個(gè)簡(jiǎn)單操作爱榕,該操作可以從一些位于內(nèi)存或磁盤的數(shù)據(jù)庫(kù)查找程序員的薪資:API 允許調(diào)用者傳入一個(gè)包含程序員名字的 String瓣喊,這會(huì)返回什么呢?從建模角度來看黔酥,它應(yīng)該返回一個(gè) Int藻三,表示程序員的年薪;但是這里有一個(gè)問題跪者,如果程序員不在數(shù)據(jù)庫(kù)中(可能根本沒有雇用她棵帽,或者已經(jīng)被解雇,要不就是輸錯(cuò)了名字……)渣玲,那么應(yīng)該返回 什么逗概。如果返回類型是 Int,則不能返回 null忘衍,這個(gè) “標(biāo)志” 通常表示沒有在數(shù)據(jù)庫(kù)中找到該用戶(您可能認(rèn)為應(yīng)該拋出一個(gè)異常仗谆,但是大多數(shù)時(shí)候數(shù)據(jù)庫(kù)丟失值并不能視為異常,因此不應(yīng)該在這里拋出異常)淑履。
在 Java 代碼中隶垮,我們最終將方法標(biāo)記為返回 java.lang.Integer,這迫使調(diào)用者知道方法可以返回 null秘噪。自然狸吞,我們可以依靠程序員來全面歸檔這個(gè)場(chǎng)景,還可以依賴程序員讀取 精心準(zhǔn)備的文檔。這類似于:我們可以要求經(jīng)理傾聽我們反對(duì)他們要求的不可能完成的項(xiàng)目期限蹋偏,然后經(jīng)理再進(jìn)一步把我們的反對(duì)傳達(dá)給上司和用戶便斥。
Scala 提供了一種普通的函數(shù)方法,打破了這一僵局威始。在某些方面枢纠,Option 類型或 Option[T],并不重視描述黎棠。它是一個(gè)具有兩個(gè)子類 Some[T] 和 None 的泛型類晋渺,用來表示 “無值” 的可能性,而不需要語(yǔ)言類型系統(tǒng)大費(fèi)周折地支持這個(gè)概念脓斩。實(shí)際上木西,使用 Option[T] 類型可以使問題更加清晰(下一節(jié)將用到)。
在使用 Option[T] 時(shí)随静,關(guān)鍵的一點(diǎn)是認(rèn)識(shí)到它實(shí)質(zhì)上是一個(gè)大小為 “1” 的強(qiáng)類型集合八千,使用一個(gè)不同的值 None 表示 “nothing” 值的可能性。因此燎猛,在這里方法沒有返回 null 表示沒有找到數(shù)據(jù)恋捆,而是進(jìn)行聲明以返回 Option[T],其中 T 是返回的原始類型重绷。那么鸠信,對(duì)于沒有查找到數(shù)據(jù)的場(chǎng)景,只需返回 None论寨,如下所示:
@Test def simpleOptionTest =
{
val footballTeamsAFCEast =
Map("New England" -> "Patriots",
"New York" -> "Jets",
"Buffalo" -> "Bills",
"Miami" -> "Dolphins",
"Los Angeles" -> null)
assertEquals(footballTeamsAFCEast.get("Miami"), Some("Dolphins"))
assertEquals(footballTeamsAFCEast.get("Miami").get(), "Dolphins")
assertEquals(footballTeamsAFCEast.get("Los Angeles"), Some(null))
assertEquals(footballTeamsAFCEast.get("Sacramento"), None)
}
注意星立,Scala Map 中 get 的返回值實(shí)際上并不對(duì)應(yīng)于傳遞的鍵。相反葬凳,它是一個(gè) Option[T] 實(shí)例绰垂,可以是與某個(gè)值有關(guān)的 Some(),也可以是 None火焰,因此可以很清晰地表示沒有在 map 中找到鍵劲装。如果它可以表示 map 上存在某個(gè)鍵,但是有對(duì)應(yīng)的 null 值昌简,這一點(diǎn)特別重要了占业。比如清單 1 中 Los Angeles 鍵。
通常纯赎,當(dāng)處理 Option[T] 時(shí)谦疾,程序員將使用模式匹配,這是一個(gè)非常函數(shù)化的概念犬金,它允許有效地 “啟用” 類型和/或值念恍,更不用說在定義中將值綁定到變量六剥、在 Some() 和 None 之間切換,以及提取 Some 的值(而不需要調(diào)用麻煩的 get() 方法)峰伙。清單 2 展示了 Scala 的模式匹配:
清單 2. 巧妙的模式匹配
@Test def optionWithPM =
{
val footballTeamsAFCEast =
Map("New England" -> "Patriots",
"New York" -> "Jets",
"Buffalo" -> "Bills",
"Miami" -> "Dolphins")
def show(value : Option[String]) =
{
value match
{
case Some(x) => x
case None => "No team found"
}
}
assertEquals(show(footballTeamsAFCEast.get("Miami")), "Dolphins")
}