日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Scala語(yǔ)言編程入門(mén)指南

《Programming Scala》由O'reilly出版社出版,是一本開(kāi)放的、不用于商業(yè)用途的Scala語(yǔ)言學(xué)習(xí)讀物,與讀書(shū)頻道介紹的《Scala程序設(shè)計(jì): Java虛擬機(jī)多核編程實(shí)戰(zhàn)》雖然不同,但是有異曲同工之妙,原文地址為:programming-scala.labs.oreilly.com/ 。本文為《Programming Scala》***章譯文,從Scala語(yǔ)言編程入門(mén)介紹開(kāi)始。

公司主營(yíng)業(yè)務(wù):成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)推出秦安免費(fèi)做網(wǎng)站回饋大家。

推薦專(zhuān)題:Scala編程語(yǔ)言

***章 從0分到60分:Scala 介紹

為什么選用 Scala

當(dāng)今的企業(yè)和互聯(lián)網(wǎng)應(yīng)用程序必須平衡一系列的要點(diǎn)。它們要有快速而可靠的實(shí)現(xiàn)。新的功能要能在短時(shí)間內(nèi)通過(guò)周期漸進(jìn)的方式加入。除了簡(jiǎn)單地提供商務(wù)邏輯之外,應(yīng)用程序必須支持訪問(wèn)安全控制,數(shù)據(jù)持久化,事務(wù),和其它的進(jìn)階功能。程序必須高度易用和可擴(kuò)展,同時(shí)要求支持并發(fā)和分布式計(jì)算。應(yīng)用程序會(huì)被網(wǎng)絡(luò)化,并且提供人和機(jī)器都易于使用的接口。

要達(dá)成這些挑戰(zhàn),許多軟件開(kāi)發(fā)者在尋找新型的編程序言和工具。以往備受推崇的如:Java,C#,和C++ 已經(jīng)不再是開(kāi)發(fā)這些次世代應(yīng)用程序的***候選。

如果你是一個(gè)Java 程序開(kāi)發(fā)者

Java 是由Sun 公司在1995 年,互聯(lián)網(wǎng)高速發(fā)展的時(shí)候正式引入的。 由于當(dāng)時(shí)需要一個(gè)安全的,可移植的,開(kāi)發(fā)者友好的程序語(yǔ)言,它被迅速地推崇為編寫(xiě)瀏覽器應(yīng)用的理想語(yǔ)言。而當(dāng)時(shí)的主流語(yǔ)言,C++,則并不適合這個(gè)領(lǐng)域。

今天,Java 被更多地使用在服務(wù)器端程序中。它是開(kāi)發(fā)網(wǎng)絡(luò)和企業(yè)應(yīng)用的***的語(yǔ)言之一。

然而,Java 是它們那個(gè)時(shí)代的產(chǎn)物,至今也有一定年代了。在1995年,Java 為了拉攏C++開(kāi)發(fā)者,提供了和C++ 足夠相似的語(yǔ)法,同時(shí)也避開(kāi)了C++ 語(yǔ)言上的缺陷和危險(xiǎn)。Java 采納了絕大多數(shù)那個(gè)時(shí)代對(duì)解決軟件開(kāi)發(fā)問(wèn)題有用的概念,比如面向?qū)ο缶幊蹋∣OP), 同時(shí)也丟棄了一些麻煩的充滿問(wèn)題的技術(shù),比如人工的內(nèi)存控制。這些設(shè)計(jì)決策在最小化復(fù)雜度和***化開(kāi)發(fā)生產(chǎn)力上達(dá)到了一個(gè)優(yōu)異的平衡。然而,從Java 出生演化到現(xiàn)在,許多人認(rèn)為它變得越來(lái)越復(fù)雜,而且并沒(méi)有顯著地解決新的程序開(kāi)發(fā)過(guò)程中面臨的問(wèn)題和挑戰(zhàn)。

程序開(kāi)發(fā)者想要一種更精煉和更靈活的語(yǔ)言去提高他們的生產(chǎn)效率。這也是如今所謂的Ruby ,Python 這樣的腳本(Script)語(yǔ)言大行其道的原因之一。

永無(wú)休止的需求驅(qū)動(dòng)著架構(gòu)向大規(guī)模并發(fā)開(kāi)發(fā)進(jìn)行。然而,Java 的并發(fā)模型是基于對(duì)共享的,可變的信號(hào)狀態(tài)的同步存取,從而導(dǎo)致了復(fù)雜的,易錯(cuò)的程序。

當(dāng)Java 漸漸老化時(shí),運(yùn)行它的 Java 虛擬機(jī)(JVM)卻持續(xù)地散發(fā)著光芒。如今JVM 的性能優(yōu)化是非凡的,它允許字節(jié)碼(byte code)在許多情況下得到和本地編譯的代碼相同的性能。今天,許多程序開(kāi)發(fā)者相信使用基于JVM 的新語(yǔ)言是正確的前進(jìn)道路。Sun 顯然是擁護(hù)這個(gè)趨勢(shì)的,他們雇傭了JRuby 和Jython (Ruby 和Python 在JVM 上的實(shí)現(xiàn))的主要開(kāi)發(fā)者。

Scala 的出現(xiàn)對(duì)于Java 開(kāi)發(fā)者來(lái)說(shuō)提供了一個(gè)更加新式的語(yǔ)言。同時(shí)保留了JVM 的驚人的性能和開(kāi)發(fā)了十幾年的Java 庫(kù)的寶貴財(cái)富。

如果你是一個(gè)Ruby,Python 的開(kāi)發(fā)者

像Ruby,Python,Groovy,JavaScript,和Smalltalk 這樣的動(dòng)態(tài)類(lèi)型語(yǔ)言,通常因?yàn)樗鼈儍?yōu)雅的靈活性,強(qiáng)大的元編程能力(metaprogramming),提供了很高的生產(chǎn)力。

如果撇開(kāi)它們?cè)诟弋a(chǎn)能上的優(yōu)勢(shì),動(dòng)態(tài)語(yǔ)言也許不是個(gè)萬(wàn)金油,特別對(duì)于大規(guī)模和高性能程序來(lái)說(shuō),不是***選擇。在編程社區(qū)里,有一個(gè)對(duì)于動(dòng)態(tài)類(lèi)型和靜態(tài)類(lèi)型究竟誰(shuí)更占有優(yōu)勢(shì)進(jìn)行的冗長(zhǎng)爭(zhēng)論。很多的比較觀點(diǎn)多少都有些主觀。我們不會(huì)在這里討論所有的這些爭(zhēng)論,但是我們會(huì)提供一些對(duì)此的看法以供參考。

相比靜態(tài)語(yǔ)言來(lái)說(shuō),優(yōu)化動(dòng)態(tài)語(yǔ)言的性能更富有挑戰(zhàn)性。在靜態(tài)語(yǔ)言中,優(yōu)化器可以根據(jù)類(lèi)型信息來(lái)進(jìn)行決策。而在動(dòng)態(tài)語(yǔ)言中,只有很有限的信息是可用的,這使得優(yōu)化器的選擇更加困難。雖然近年來(lái)在動(dòng)態(tài)語(yǔ)言優(yōu)化方面的提升漸漸浮現(xiàn)希望,但是它們還是落在靜態(tài)語(yǔ)言的優(yōu)化藝術(shù)的后面。所以,如果你確實(shí)需要很高的性能,靜態(tài)語(yǔ)言或許是一個(gè)更保險(xiǎn)的選擇。

靜態(tài)語(yǔ)言同樣可以使開(kāi)發(fā)過(guò)程獲得好處。集成開(kāi)發(fā)環(huán)境(IDE)的一些功能,比如自動(dòng)完成(有時(shí)候被稱(chēng)為智能感知)在靜態(tài)語(yǔ)言中更容易完成,因?yàn)槟切╊?lèi)型信息都是可用的。而更加明顯的類(lèi)型信息在靜態(tài)代碼中促進(jìn)了代碼的自我解釋?zhuān)S著項(xiàng)目的發(fā)展,這對(duì)于開(kāi)發(fā)者意圖的互相交流是十分重要的。

當(dāng)使用一種靜態(tài)語(yǔ)言時(shí),你必須時(shí)刻考慮使用恰當(dāng)?shù)念?lèi)型。這迫使你在選擇設(shè)計(jì)時(shí)更加小心。這雖然會(huì)拖慢日常的設(shè)計(jì)決策,但是長(zhǎng)此以往,在應(yīng)用程序中對(duì)類(lèi)型使用的思考會(huì)帶來(lái)更為清晰的設(shè)計(jì)。

靜態(tài)語(yǔ)言的另外一個(gè)小的好處就是編譯時(shí)期的額外檢查。我們通常認(rèn)為這個(gè)優(yōu)勢(shì)被夸大了,因?yàn)轭?lèi)型不匹配的錯(cuò)誤只是日常見(jiàn)到的運(yùn)行時(shí)錯(cuò)誤中的一小部分。編譯器無(wú)法發(fā)現(xiàn)邏輯錯(cuò)誤,這顯然更加重要。只有一個(gè)綜合的,自動(dòng)的測(cè)試組可以發(fā)現(xiàn)邏輯錯(cuò)誤。對(duì)于動(dòng)態(tài)語(yǔ)言來(lái)說(shuō),測(cè)試也必須覆蓋可能的類(lèi)型錯(cuò)誤。如果你以前編寫(xiě)過(guò)動(dòng)態(tài)類(lèi)型語(yǔ)言,你會(huì)發(fā)現(xiàn)你的測(cè)試組其實(shí)會(huì)小一些,但不會(huì)小很多。

許多開(kāi)發(fā)者發(fā)現(xiàn)靜態(tài)語(yǔ)言太過(guò)冗長(zhǎng),抱怨靜態(tài)類(lèi)型是冗長(zhǎng)的元兇,而事實(shí)上真正的原因是缺少類(lèi)型推斷。在類(lèi)型推斷的情況下,編譯器會(huì)根據(jù)上下文推斷值的類(lèi)型。例如,編譯器會(huì)識(shí)別在 x = 1 + 3 中x 是一個(gè)整型。類(lèi)型推斷能顯著地減少代碼的長(zhǎng)度,使得代碼更像是用動(dòng)態(tài)語(yǔ)言編寫(xiě)出來(lái)的。

我們都曾經(jīng)在不同的時(shí)間和靜態(tài)語(yǔ)言和動(dòng)態(tài)語(yǔ)言打過(guò)交道。我們發(fā)現(xiàn)兩種類(lèi)型的語(yǔ)言都因?yàn)椴煌脑虮粡V為關(guān)注。我們相信現(xiàn)代軟件開(kāi)發(fā)者必須掌握一系列的語(yǔ)言和工具。有時(shí),動(dòng)態(tài)語(yǔ)言會(huì)是完成工作的正確工具;而有時(shí),一個(gè)靜態(tài)語(yǔ)言,例如Scala,會(huì)是你正需要的。

#p#

Scala 介紹

Scala 是一種迎合現(xiàn)代軟件開(kāi)發(fā)者需求的語(yǔ)言。它是靜態(tài)的,混合范式的(mixed-paradigm),基于JVM 的語(yǔ)言;它在擁有簡(jiǎn)潔,優(yōu)雅,靈活的語(yǔ)法的同時(shí),也提供了一個(gè)久經(jīng)考驗(yàn)的類(lèi)型系統(tǒng)和慣用語(yǔ)法,所以從小巧的解釋性腳本到大型的復(fù)雜系統(tǒng)它都可以勝任。那可是一大口蛋糕,所以,讓我們?cè)敿?xì)地來(lái)了解下它的一些特性。

靜態(tài)類(lèi)型

正如我們?cè)谇懊娴恼鹿?jié)所描述的,一個(gè)靜態(tài)類(lèi)型的語(yǔ)言在一個(gè)變量的生命周期內(nèi)都會(huì)綁定一個(gè)類(lèi)型。相反的,動(dòng)態(tài)類(lèi)型的語(yǔ)言則是根據(jù)變量所引用的值來(lái)綁定類(lèi)型,這意味著變量的類(lèi)型可以隨著它引用的值改變而改變。

在***的基于JVM 的語(yǔ)言中,Scala 是為數(shù)不多的靜態(tài)類(lèi)型語(yǔ)言,而且是最出名的一個(gè)。

混合范式 - 面向?qū)ο缶幊?/strong>

Scala 完全支持面向?qū)ο缶幊蹋∣OP)。Scala 在改進(jìn)Java 對(duì)OOP 的支持的同時(shí),添加了traits (特性)的概念,它可以簡(jiǎn)潔地實(shí)現(xiàn)類(lèi)之間的混合關(guān)系。Scala 的traits 和Ruby 的modules (模塊)概念類(lèi)似。如果你是一個(gè)Java 開(kāi)發(fā)者,可以把traits 想象成interfaces (接口)和implementations (實(shí)現(xiàn))的統(tǒng)一體。

在Scala 中,所有的東西實(shí)際上都是一個(gè)object (對(duì)象)。Scala 不像Java,它沒(méi)有原始類(lèi)型(元數(shù)據(jù)類(lèi)型)。相反的,所有的數(shù)值類(lèi)型都是正真的objects。 然而,為了優(yōu)化性能,Scala 會(huì)實(shí)時(shí)地在底層實(shí)現(xiàn)中使用原始類(lèi)型。另外,Scala 不支持static (靜態(tài))或者class-level members (類(lèi)級(jí)別成員)的類(lèi)型,因?yàn)樗鼈儾](méi)有和一個(gè)實(shí)例(instance)關(guān)聯(lián)。相反,Scala 支持單例模式,可以應(yīng)用于那些一種類(lèi)型只有一個(gè)實(shí)例的情況。

混合范式 - 函數(shù)式編程

Scala 完全支持函數(shù)式編程(FP)。FP 是一種比OOP 更加古老的編程范式,它被學(xué)術(shù)界的象牙塔庇護(hù)至今。FP 因?yàn)楹?jiǎn)化了某些設(shè)計(jì),尤其是并發(fā)上的問(wèn)題而受到了越來(lái)越多的關(guān)注?!凹兇狻钡暮瘮?shù)式語(yǔ)言不允許任何易變狀態(tài)(mutable state),因而避免了對(duì)易變狀態(tài)的同步和共享訪問(wèn)。相反的,用純函數(shù)式語(yǔ)言編寫(xiě)的程序在并發(fā)自主的進(jìn)程中通過(guò)傳遞消息來(lái)通信。Scala 通過(guò)Actors 庫(kù)來(lái)支持這種模式,但是它同時(shí)允許mutable (易變的)和immutable (不易變的)變量。

函數(shù)是FP 的一類(lèi)公民,這意味著它們可以被賦值給變量,被傳遞給其他函數(shù)等,就像普通的值一樣。這個(gè)特色允許通過(guò)元操作來(lái)組合一些高級(jí)行為。因?yàn)镾cala 遵守所有的東西都是object 的原則,函數(shù)在Scala 中也是objects。

Scala 同時(shí)支持閉包,一種動(dòng)態(tài)語(yǔ)言比如Python 和Ruby 從函數(shù)式編程世界中引用過(guò)來(lái)的特性。Java 很遺憾地沒(méi)有在最近的版本中包含這個(gè)特性。閉包本質(zhì)上是一個(gè)函數(shù)和其引用的變量的統(tǒng)一定義。這些變量不作為傳入?yún)?shù)或者函數(shù)內(nèi)的局部變量。一個(gè)閉包封閉了這些引用,所以函數(shù)調(diào)用可以安全的引用這些變量,即使它們已經(jīng)超出了函數(shù)的作用域。閉包是一個(gè)強(qiáng)大的抽象,以至于objects 系統(tǒng)和基礎(chǔ)控制結(jié)構(gòu)經(jīng)常是用它們實(shí)現(xiàn)的。

一種同時(shí)有JVM 和.NET 實(shí)現(xiàn)的語(yǔ)言

Scala 是眾所周知的基于JVM 的語(yǔ)言,這意味著Scala 會(huì)生成JVM 字節(jié)碼。一個(gè)能生成CLR 字節(jié)碼的基于.NET 的Scala 版本也同時(shí)在開(kāi)發(fā)中。當(dāng)我們提到底層的“運(yùn)行時(shí)”時(shí),我們通常是指JVM。但是許多我們提到的概念能同時(shí)運(yùn)用于兩種不同的運(yùn)行時(shí)。當(dāng)我們討論有關(guān)JVM 的細(xì)節(jié)時(shí),它們大致也能應(yīng)用于.NET,除非我們特別說(shuō)明。

Scala 的編譯器使用了一些聰明的技巧來(lái)映射Scala 的擴(kuò)展到相應(yīng)的字節(jié)碼。在Scala 里,我們可以輕松地調(diào)用產(chǎn)生自Java 源代碼(JVM)或者C# 源代碼(.NET)的字節(jié)碼。同樣的,你也可以在Java,C# 代碼里調(diào)用Scala。運(yùn)行在JVM 和CLR 上,允許Scala 開(kāi)發(fā)者來(lái)充分利用現(xiàn)有的庫(kù)來(lái)和運(yùn)行在這些運(yùn)行時(shí)的其他語(yǔ)言交互。

簡(jiǎn)潔的,優(yōu)雅的,靈活的語(yǔ)法

Java 的語(yǔ)法實(shí)際上有些冗長(zhǎng)。 Scala 使用了一些技巧來(lái)減少不必要的語(yǔ)法,使得Scala 源碼幾乎和其他的動(dòng)態(tài)語(yǔ)言一樣簡(jiǎn)潔。類(lèi)型推斷使得顯式的類(lèi)型聲明信息在大多數(shù)場(chǎng)合下減少到了***。類(lèi)型和函數(shù)的聲明變得非常簡(jiǎn)潔。

Scala 允許函數(shù)名字包含非字母數(shù)字的字符。結(jié)合一些語(yǔ)法上的技巧,這些特性允許用戶定義一些看起來(lái)像操作符的方法。這樣,在語(yǔ)言核心之外的庫(kù)對(duì)于用戶看來(lái)就會(huì)比較自然。

復(fù)雜的類(lèi)型系統(tǒng)

Scala 擴(kuò)展了Java 的類(lèi)型系統(tǒng),同時(shí)提供了更靈活的類(lèi)型和一些高級(jí)的類(lèi)型結(jié)構(gòu)。這個(gè)類(lèi)型系統(tǒng)起初開(kāi)起來(lái)可能會(huì)有些恐怖,但是大多數(shù)時(shí)候你不用擔(dān)心這些高級(jí)的結(jié)構(gòu)。類(lèi)型推斷幫助你自動(dòng)推斷類(lèi)型簽名,所以用戶不用人工提供一般的類(lèi)型信息。不過(guò),當(dāng)你需要它們的時(shí)候,高級(jí)類(lèi)型特性可以給你提供更靈活的方式,用類(lèi)型安全的方式解決設(shè)計(jì)上的問(wèn)題。

可伸縮性 - 架構(gòu)

Scala 被設(shè)計(jì)來(lái)服務(wù)從小的,解釋性腳本到大型的,分布式系統(tǒng)。Scala 提供了4 種語(yǔ)言機(jī)制來(lái)提供更靈活的系統(tǒng)組合:1)顯式的自我類(lèi)型(selftype),2)類(lèi)型成員和種類(lèi)的抽象,3)嵌套的類(lèi),以及4)使用traits 的混合結(jié)構(gòu)。

沒(méi)有其它的語(yǔ)言同時(shí)提供所有這些機(jī)制了。這些機(jī)制一起允許用一種類(lèi)型安全和簡(jiǎn)潔的方式來(lái)構(gòu)建由可重用組件組成的程序。正如我們所見(jiàn),許多常見(jiàn)的設(shè)計(jì)模式和架構(gòu)技術(shù),例如依賴注入模式,可以容易地用Scala 來(lái)實(shí)現(xiàn),而不用冗長(zhǎng)的樣板代碼或者XML 配置文件 -- 它們經(jīng)常讓Java 開(kāi)發(fā)變得很枯燥。

可伸縮性 - 性能

因?yàn)镾cala 代碼運(yùn)行在JVM 和CLR 上,它能獲得所有來(lái)自這些運(yùn)行時(shí)和支持性能靈活性調(diào)整的第三方工具的性能優(yōu)化,例如分析器(profiler),分布式緩存庫(kù),集群機(jī)制等。如果你相信Java 和C# 的性能,那么你就能信任Scala 的性能。當(dāng)然,一些特別的結(jié)構(gòu)在這個(gè)語(yǔ)言環(huán)境下和某些庫(kù)中相比其它語(yǔ)言會(huì)運(yùn)行地特別高效或者低效。一如既往的,你應(yīng)該在必要的時(shí)候分析和優(yōu)化你的代碼。

表面上看起來(lái)OOP 和FP 像是不兼容的。但實(shí)際上,Scala 的設(shè)計(jì)哲學(xué)是OOP 和FP 應(yīng)該協(xié)同合作而不是對(duì)立。其中一方的特性應(yīng)該能增強(qiáng)另一方。

在FP 里,函數(shù)沒(méi)有副作用,變量都是不易變的。而在OOP 中,可變狀態(tài)和副作用都十分常見(jiàn),甚至是被鼓勵(lì)的。Scala 讓你來(lái)選擇解決設(shè)計(jì)問(wèn)題***的方式。函數(shù)式編程對(duì)于并發(fā)特別有用,因?yàn)樗饤壛藢?duì)易變狀態(tài)的同步訪問(wèn)。然而,“純”函數(shù)式語(yǔ)言是十分嚴(yán)格的。有一些設(shè)計(jì)問(wèn)題還是使用易變對(duì)象比較容易解決。

Scala 的名字來(lái)自詞語(yǔ):可伸縮的語(yǔ)言(scalable language)的縮寫(xiě)。這就建議Scala 的發(fā)音為scale-ah,實(shí)際上Scala 的創(chuàng)建者發(fā)音為scah-lah,像意大利語(yǔ)中的“stairs”(樓梯)。也就是說(shuō),兩個(gè)“a 的”的發(fā)音是一樣的。

Martin Oderskey  的在計(jì)算機(jī)語(yǔ)言方面的背景和經(jīng)驗(yàn)是顯著的。在你學(xué)習(xí)Scala 的時(shí)候,你會(huì)了解這是一個(gè)仔細(xì)考慮了設(shè)計(jì)決策,利用了所有類(lèi)型理論,OOP 和FP 的藝術(shù)的語(yǔ)言。Martin 在JVM 方面的經(jīng)驗(yàn)對(duì)于Scala 和JVM 平臺(tái)的優(yōu)雅結(jié)合有著顯著的幫助。它綜合了OOP 和FP 的優(yōu)點(diǎn),是一個(gè)優(yōu)秀的兩全其美的解決方案。

Scala 的誘惑

今天,我們的產(chǎn)業(yè)幸運(yùn)地?fù)碛性S多語(yǔ)言方面的選擇。動(dòng)態(tài)語(yǔ)言的能力,靈活性,優(yōu)雅已經(jīng)使它們十分流行。但是,Java 和.NET 庫(kù),已經(jīng)JVM 和CLR 的性能作為珍貴的寶藏,符合了許多實(shí)際的企業(yè)和互聯(lián)網(wǎng)項(xiàng)目的需求。

Scala 引起眾人的興趣是因?yàn)樗暮?jiǎn)潔語(yǔ)法和類(lèi)型推斷,看起來(lái)像動(dòng)態(tài)腳本語(yǔ)言。但是,Scala 提供了所有靜態(tài)類(lèi)型的優(yōu)勢(shì),一個(gè)現(xiàn)代的對(duì)象模型,函數(shù)式編程,和先進(jìn)的類(lèi)型系統(tǒng)。這些工具允許你建立一個(gè)可伸縮的,模塊化的應(yīng)用程序,并且重用之前的Java 和.NET API, 充分利用JVM 和CLR 的性能。

Scala 是面向?qū)I(yè)開(kāi)發(fā)者的語(yǔ)言。相比較與Java 和Ruby,Scala 更難掌握。因?yàn)樗驩OP,F(xiàn)P 和靜態(tài)類(lèi)型方面的技能,這樣才能更高效地使用它。它誘使我們偏愛(ài)動(dòng)態(tài)語(yǔ)言的相對(duì)簡(jiǎn)潔。但是,這種簡(jiǎn)潔是一種假象。在一種動(dòng)態(tài)類(lèi)型語(yǔ)言中,使用元編程特性來(lái)實(shí)現(xiàn)高級(jí)設(shè)計(jì)經(jīng)常是必要的。元編程十分強(qiáng)大,但是使用它需要經(jīng)驗(yàn),而且會(huì)導(dǎo)致代碼變得難以理解,維護(hù)和調(diào)試。在Scala 中,許多類(lèi)似的設(shè)計(jì)目標(biāo)可以通過(guò)類(lèi)型安全的方式來(lái)達(dá)到,利用它的類(lèi)型系統(tǒng)和通過(guò)traits 實(shí)現(xiàn)的混合結(jié)構(gòu)。

我們覺(jué)得在Scala 的日常使用中所需求的額外努力會(huì)迫使我們?cè)谠O(shè)計(jì)時(shí)更加謹(jǐn)慎。久而久之,這樣的幾率會(huì)導(dǎo)致更加清晰的,模塊化的,可維護(hù)的系統(tǒng)。幸運(yùn)的是,你不必所有時(shí)候都去追逐Scala 所有復(fù)雜的功能。你的大多數(shù)代碼會(huì)簡(jiǎn)單清晰,就像是用你最喜歡的動(dòng)態(tài)語(yǔ)言寫(xiě)出來(lái)的一樣。

另外一個(gè)策略是聯(lián)合幾種簡(jiǎn)單的語(yǔ)言,比如Java 來(lái)做面向?qū)ο蟮拇a,Erlang 來(lái)做函數(shù)式,并發(fā)的代碼。這樣一個(gè)分解會(huì)工作的很好,如果你的系統(tǒng)能清晰地分解成這些不想關(guān)聯(lián)的部分,并且你的團(tuán)隊(duì)能掌控這樣一個(gè)混雜的環(huán)境。Scala 對(duì)于那些僅需要一個(gè)全能語(yǔ)言的情況是***吸引力的。也就是說(shuō),Scala 代碼可以和諧地與其他語(yǔ)言共處,尤其是基于JVM 和.NET 的語(yǔ)言。

#p#

安裝 Scala

這個(gè)章節(jié)描述了如何安裝Scala 的命令行工具, 以便可以盡快讓Scala 跑起來(lái),這也是運(yùn)行本書(shū)所有范例的必要充分條件。在各種編輯器和集成開(kāi)發(fā)環(huán)境(IDE)中使用Scala 的細(xì)節(jié),請(qǐng)參見(jiàn)和IDE 集成,在第14章-Scala 工具,庫(kù)和IDE 支持。本書(shū)的范例是用Scala 版本2.7.5.final 來(lái)編寫(xiě)和編譯, 也是本書(shū)在編寫(xiě)時(shí)候的***的發(fā)行版;也有部分是用Scala 版本2.8.0 的每夜編譯版本,當(dāng)你讀到這本書(shū)的時(shí)候應(yīng)該已經(jīng)最終完成了。

注意

2.8 版本引入了很多新的特性,我們會(huì)在這本書(shū)中予以強(qiáng)調(diào)。

我們?cè)谶@本書(shū)中會(huì)選用JVM 版本的Scala。 首先,你必須安裝Java 1.4 或更高的版本(推薦1.5 或更高)。如果你需要安裝Java,請(qǐng)登錄,按照指示在你的電腦上安裝Java。

Scala 的官方網(wǎng)站 。要安裝Scala,去到下載(downloads)頁(yè)面 。按照下載頁(yè)面上的指示下載適合你系統(tǒng)環(huán)境的安裝包。

最簡(jiǎn)單的跨平臺(tái)安裝包是IzPack 安裝器。下載Scala 的jar (譯注:編譯完以后的Java 專(zhuān)屬格式)文件,可以選擇scala-2.7.5.final-installer.jar 或者 scala-2.8.0.N-installer.jar, N在這里是2.8.0 版本的***發(fā)布版本。在終端窗口中(譯注:或者Windwos 下的命令行),定位到下載的目錄,使用java 命令來(lái)安裝Scala。假設(shè)你下載了scala-2.8.0.final-installer.jar,運(yùn)行如下命令會(huì)帶領(lǐng)你完成安裝過(guò)程。

 
 
 
  1.  
  2.  
  3. java -jar scala-2.8.0.final-installer.jar  

提示

在蘋(píng)果系統(tǒng)下(Mac OS X),安裝Scala 的最簡(jiǎn)單方式是使用MacPorts。按照這個(gè)頁(yè)面的安裝指示,然后使用 sudo port insall scala. 不用幾分鐘你就可以運(yùn)行Scala 了。

在本書(shū)中,我們會(huì)使用符號(hào)scala-home 來(lái)指定Scala 安裝路徑的根目錄。

注意

在Unix,Linux,和Mac OS X 系統(tǒng)下,你需要root 用戶權(quán)限,或者sudo 命令才能把Scala 安裝在一個(gè)系統(tǒng)目錄下。比如:

 
 
 
  1. scala-home = /usr/local/scala-2.8.0.final. 

或者,你也可以下載并且展開(kāi)壓縮過(guò)的tar 文件(比如scala-2.8.0.final.tgz)或者zip 文件(scala-2.8.0.final.zip)。在類(lèi)Unix 系統(tǒng)中,展開(kāi)壓縮文件到一個(gè)你選擇的路徑。然后,把scala-home/bin 子目錄加入到你的PATH 環(huán)境變量中。例如,如果你安裝到 /usr/local/scala-2.8.0.final,那么把/usr/local/scala-2.8.0.final/bin 加入到PATH。

要測(cè)試你的安裝,在命令行下運(yùn)行如下命令:

 
 
 
  1.  
  2.  
  3. scala -version  

我們會(huì)在后面學(xué)習(xí)如何使用scala 命令行。你應(yīng)該能獲得如下輸出:

 
 
 
  1.  
  2.  
  3. Scala code runner version 2.8.0.final -- Copyright 2002-2009, LAMP/EPFL  

當(dāng)然,你看到的版本號(hào)會(huì)根據(jù)你安裝的版本而改變。從現(xiàn)在起,當(dāng)我們展示命令行輸出時(shí)候如果包含版本號(hào),我們會(huì)使用2.8.0.final。

祝賀你,你已經(jīng)安裝了Scala!如果你在運(yùn)行scala 后獲得一個(gè)錯(cuò)誤消息:command not found(無(wú)法找到命令),重新檢查你的PATH 環(huán)境變量,確保它被正確地設(shè)立,并包含了正確的bin 目錄。

注意

Scala 2.7.X 以及之前的版本和JDK 1.4 以及更新的版本兼容。Scala 2.8 版本舍棄了1.4 的兼容性。注意Scala 會(huì)使用原生的JDK 類(lèi)庫(kù),比如String 類(lèi)。在.NET 下,Scala 使用對(duì)應(yīng)的.NET 類(lèi)。

同時(shí),你應(yīng)該能在那個(gè)下載頁(yè)面找到Scala API 文檔和源碼的下載鏈接。

更多信息

在探索Scala 的時(shí)候,你會(huì)在這個(gè)網(wǎng)頁(yè)上發(fā)現(xiàn)有用的資源。你會(huì)發(fā)現(xiàn)開(kāi)發(fā)支持工具和庫(kù)的鏈接,還有教程,語(yǔ)言規(guī)范【ScalaSpec2009】,和描述語(yǔ)言特性的學(xué)術(shù)論文。

Scala 工具和API 的文檔特別有用。你可以在這個(gè)頁(yè)面瀏覽API 文檔。這個(gè)文檔是使用scaladoc 工具生成的,類(lèi)似于Java 的javadoc 工具。參見(jiàn)第14章- Scala 工具,庫(kù)和IDE支持的“scaladoc 命令行工具”來(lái)獲得更多的信息。

你也可以在下面頁(yè)面下載一個(gè)API 文檔的壓縮文件來(lái)本地瀏覽?;蛘吣憧梢杂胹baz 工具來(lái)安裝,如下

 
 
 
  1. sbaz install scala-devel-docs 

sbaz 和scala,scalac 命令行工具安裝同樣的bin 目錄下。安裝的文檔也同樣包含了scala 工具集(包括sbaz)的細(xì)節(jié)和代碼示例。要獲取更多Scala 命令行工具和其他資源的信息,參見(jiàn)第14章- Scala 工具,庫(kù)和IDE 支持。

#p#

初嘗 Scala

是時(shí)候用一些實(shí)在的Scala 代碼來(lái)刺激一下你的欲望了。在下面的范例中,我們會(huì)描述足夠的細(xì)節(jié)讓你明白發(fā)生了什么。這一節(jié)的目標(biāo)是給你一個(gè)大致概念,讓你知道用Scala 來(lái)編程是怎么一回事。我們會(huì)在以后的各個(gè)章節(jié)來(lái)探索Scala 更多的特性。

作為***個(gè)實(shí)例,你可以用兩種方式來(lái)運(yùn)行它:交互式的,或者作為一個(gè)“腳本”。

讓我們從交互式模式開(kāi)始。我們可以通過(guò)在命令行輸入scala,回車(chē),來(lái)啟動(dòng)scala 解釋器。你會(huì)看到如下輸出。(版本號(hào)可能會(huì)有所不同。)

 
 
 
  1. Welcome to Scala version 2.8.0.final (Java ...).  
  2. Type in expressions to have them evaluated.  
  3. Type :help for more information.  
  4. scala> 

***一行是等待你輸入的提示符。交互式的scala 命令對(duì)于實(shí)驗(yàn)來(lái)說(shuō)十分方便(參見(jiàn)《第14章-Scala工具,庫(kù)和IDE支持》的“Scala 命令行工具”章節(jié),來(lái)獲取更多信息)。一個(gè)像這樣的交互式解釋器被稱(chēng)為REPL:讀(Read),評(píng)估(Evaluate),打?。≒rint),循環(huán)(Loop)

輸入如下的兩行代碼。

 
 
 
  1. val book = "Programming Scala" 
  2. println(book) 

實(shí)際上的輸入和輸出看起來(lái)會(huì)像是這樣。

 
 
 
  1. scala> val book = "Programming Scala" 
  2. book: java.lang.String = Programming Scala  
  3. scala> println(book)  
  4. Programming Scala  
  5. scala> 

在***行我們使用了val 關(guān)鍵字來(lái)聲明一個(gè)只讀變量 book。注意解釋器的輸出顯示了book 的類(lèi)型和值。這對(duì)理解復(fù)雜的聲明會(huì)很方便。第二行打印出了book 的值 -- Programming Scala。

提示

在交互模式(REPL)模式下來(lái)測(cè)試scala 命令是學(xué)習(xí)Scala 細(xì)節(jié)的一個(gè)非常好的方式。

這本書(shū)里的許多例子可以像這樣在解釋器里運(yùn)行。然而,通常使用我們提到的第二個(gè)方式會(huì)更加方便:在文本編輯器中或者IDE 中編寫(xiě)Scala 腳本,然后用同樣的scala 命令來(lái)執(zhí)行它們。我們會(huì)在這章余下的絕大多數(shù)部分使用這種方式。

用你選擇的文本編輯器,保存下面例子中的Scala 代碼到一個(gè)名為upper1-script.scala 的文件,放在你選擇的目錄中。

 
 
 
  1. // code-examples/IntroducingScala/upper1-script.scala  
  2. class Upper {  
  3.   def upper(strings: String*): Seq[String] = {  
  4.     strings.map((s:String) => s.toUpperCase())  
  5.   }  
  6. }  
  7. val up = new Upper  
  8. Console.println(up.upper("A", "First", "Scala", "Program")) 

這段Scala 腳本把一個(gè)字符串轉(zhuǎn)換到大寫(xiě)。

順便說(shuō)一下,在***行有一句注釋?zhuān)ㄔ诖a例子中是源文件的名字)。Scala 使用和Java,C#,C++等一樣的注釋方式。一個(gè)// 注釋會(huì)影響整個(gè)一行,而/* 注釋 */ 方式則可以跨行。

要運(yùn)行這段腳本,打開(kāi)命令行窗口,定位到對(duì)應(yīng)目錄,然后運(yùn)行如下命令。

 
 
 
  1. scala upper1-script.scala 

文件會(huì)被解釋?zhuān)@意味著它會(huì)被編譯和執(zhí)行。你會(huì)獲得如下輸出:

 
 
 
  1. Array(A, FIRST, SCALA, PROGRAM) 

解釋 VS 編譯,運(yùn)行Scala 代碼

總的來(lái)說(shuō),如果你在命令行輸入scala 而不輸入文件名參數(shù),解釋器會(huì)運(yùn)行在交互模式。你輸入的定義和語(yǔ)句會(huì)被立即執(zhí)行。如果你附帶了一個(gè)scala 文件作為命令參數(shù),它會(huì)把文件作為腳本編譯和運(yùn)行,就像我們的 scala upper1-script.scala 例子一樣。***,你可以單獨(dú)編譯scala 文件,運(yùn)行class 文件,只要你有一個(gè)main 函數(shù),就像你通常使用java 命令一樣。(我們會(huì)馬上給出一個(gè)例子)

你需要理解有關(guān)使用解釋模式的局限和單獨(dú)編譯運(yùn)行之間的一些微妙的區(qū)別。我們會(huì)在《第14章- Scala 工具,庫(kù)和IDE 支持》的“命令行工具”部分詳細(xì)解釋這些區(qū)別。

當(dāng)我們提及執(zhí)行一個(gè)腳本時(shí),就是說(shuō)用scala 命令運(yùn)行一個(gè)Scala 源文件。

在這個(gè)例子里,類(lèi)Upper (字面意思,沒(méi)有雙關(guān)) 里的upper 函數(shù)把輸入字符串轉(zhuǎn)換為大寫(xiě),然后作為一個(gè)數(shù)組返回。***一行把4個(gè)字符串轉(zhuǎn)換完以后輸出。

為了學(xué)習(xí)Scala 語(yǔ)法,讓我們來(lái)更詳細(xì)地解釋一下代碼。在這僅有的6行代碼里面有許多細(xì)節(jié)!我們會(huì)解釋一下基礎(chǔ)的概念。這個(gè)例子的所有的概念會(huì)在這被書(shū)的后面幾個(gè)章節(jié)被詳細(xì)地講解。

在這個(gè)例子里,Upper 類(lèi)以class 關(guān)鍵字開(kāi)始。類(lèi)的主體被概括在最外面的大括號(hào)中 {...}。

upper 方法的定義在二行,以def 關(guān)鍵字開(kāi)始,緊接著是方法名,參數(shù)列表,和方法的返回類(lèi)型,***是等于號(hào)“=”,和方法的主體。

在括號(hào)里的參數(shù)列表實(shí)際上是一個(gè)String(字符串)類(lèi)型的可變長(zhǎng)度參數(shù)列表,由冒號(hào)后面后面的String* 類(lèi)型決定。也就是說(shuō),你可以傳入任意多的,以分號(hào)分隔的字符串(包括空的列表)。這些字符串會(huì)被存在一個(gè)名為strings 的參數(shù)中。在這個(gè)方法里面,strings 實(shí)際上是一個(gè)Array(數(shù)組)。

注意

當(dāng)在代碼里顯式地為變量指定類(lèi)型信息時(shí),類(lèi)型注解應(yīng)該跟在變量名的冒號(hào)后面(也就是類(lèi)Pascal 語(yǔ)法)。Scala 為什么不遵照J(rèn)ava 的慣例呢? 回想一下,類(lèi)型信息在Scala 中經(jīng)常是被推斷出來(lái)的(不像Java),這意味著我們并不總是需要顯式的聲明類(lèi)型。和Java 的類(lèi)型習(xí)慣比較,item: type 模式在你忽略掉冒號(hào)和類(lèi)型注解的時(shí)候,更容易被編譯器清楚地分析。

方法的返回類(lèi)型在參數(shù)列表的***出現(xiàn)。在這個(gè)例子里,返回類(lèi)型是Seq[String],Seq(sequence)是一種特殊的集合。它是參數(shù)化的類(lèi)型(像Java 中的泛型),在這里String 是參數(shù)。注意,Scala 使用方括號(hào)[...] 來(lái)指定參數(shù)類(lèi)型,而Java 使用尖括號(hào)<...>。

注意

Scala 允許在方法名中使用尖括號(hào),比如命名“小于”方法為<,這很常見(jiàn)。所以,為了避免二義性,Scala 使用了方括號(hào)來(lái)聲明參數(shù)類(lèi)型。它們不能被用于方法名。這就是為什么Scala 不允許像Java 那樣的使用尖括號(hào)的習(xí)慣。

upper 方法的主體跟在等于號(hào)“=”后面。為什么是一個(gè)等于號(hào)?為什么不像Java 一樣直接使用大括號(hào){...} 呢?因?yàn)榉痔?hào),函數(shù)返回類(lèi)型,方法參數(shù)列表,甚至大括號(hào)都經(jīng)常會(huì)被省略,使用等于號(hào)可以避免幾種可能的二義性。使用等于號(hào)也提醒了我們,即使是函數(shù),在Scala 里面也是值。這和Scala 對(duì)函數(shù)是編程的支持是一致的。我們會(huì)在《第8章,Scala 函數(shù)式編程》里討論更多的細(xì)節(jié)。

函數(shù)的主體調(diào)用了strings 數(shù)組的map 方法,它接受一個(gè)字面函數(shù)(Function Literal)作為參數(shù)。字面函數(shù)也就是“匿名”函數(shù)。它們類(lèi)似于其它語(yǔ)言中的Lambda 表達(dá)式,閉包,塊,或者過(guò)程。在Java 里,你可能會(huì)在這里使用一個(gè)匿名內(nèi)部類(lèi)來(lái)實(shí)現(xiàn)一個(gè)接口(interface)定義的方法。

在這個(gè)例子里,我們傳入這樣的一個(gè)字面函數(shù)。

 
 
 
  1. (s:String) => s.toUpperCase() 

它接受一個(gè)單獨(dú)的名為s 的String 類(lèi)型參數(shù). 函數(shù)的主體在“箭頭” => 的后面。它調(diào)用了s 的toUpperCase() 方法。調(diào)用的結(jié)果會(huì)被函數(shù)返回。在Scala 中,函數(shù)的***一個(gè)表達(dá)式就是返回值,盡管你也可以在其它地方使用return 語(yǔ)句。return 關(guān)鍵字在這里是可選的,而且很少被用到,除非在一段代碼中間返回(比如在一個(gè)if 語(yǔ)句塊中)。

注意

***一個(gè)表達(dá)式的值是默認(rèn)的返回值。不需要顯式的return。

繼續(xù),map 把strings 里面的每一個(gè)String 傳遞給字面函數(shù),從而用這些返回值創(chuàng)建了一個(gè)新的集合。

要運(yùn)行這些代碼,我們創(chuàng)建一個(gè)新的Upper 實(shí)例,然后把它賦值給一個(gè)名為up 的變量。和Java,C#,以及其它類(lèi)似的語(yǔ)言一樣,語(yǔ)法new Upper 創(chuàng)建了一個(gè)新的實(shí)例。變量up 被val 關(guān)鍵字定義為一個(gè)只讀的值。

***,我們對(duì)一個(gè)字符串列表調(diào)用upper 方法,然后用Console.println(...) 方法打印出來(lái)。這和Java 的System.out.println(...) 等效。

實(shí)際上,我們可以更加簡(jiǎn)化我們的代碼。來(lái)看下面這一段簡(jiǎn)化版的腳本。

 
 
 
  1. // code-examples/IntroducingScala/upper2-script.scala  
  2.  
  3. object Upper {  
  4.   def upper(strings: String*) = strings.map(_.toUpperCase())  
  5. }  
  6. println(Upper.upper("A", "First", "Scala", "Program"))  

這段代碼做了一模一樣的事情,但是用了更少的字符。

在***行,Upper 被定義為一個(gè)object,也就是單體模式。實(shí)際上我們是定義了一個(gè)class,但是Scala 運(yùn)行時(shí)僅會(huì)創(chuàng)建Upper 的一個(gè)實(shí)例。(比如,你就不能寫(xiě)new Upper了。)Scala 的objects 被使用在其他語(yǔ)言需要“類(lèi)級(jí)別”的成員的時(shí)候,比如Java 的statics (靜態(tài)成員)。我們實(shí)際上并不需要更多的實(shí)例,所以單體模式也不錯(cuò)。

注意

Scala 為什么不支持statics?因?yàn)樵赟cala 中,所有的東西都是一個(gè)object,object 結(jié)構(gòu)使得這樣的政策保持了一致。Java 的static 方法和字段并不綁定到一個(gè)實(shí)際的實(shí)例。

注意這樣的代碼是完全線程安全的。我們沒(méi)有定義任何會(huì)引起線程安全問(wèn)題的變量。我們使用的API 方法也是線程安全的。所以,我們不需要多個(gè)實(shí)例。單體模式工作的很好。

在第二行的upper 方法的實(shí)現(xiàn)也變簡(jiǎn)單了。Scala 通常可以推斷出方法的返回值(但是方法參數(shù)的類(lèi)型就不行了),所以我們不用顯式聲明。而且,因?yàn)樵诜椒ǖ闹黧w中只有一個(gè)表達(dá)式,我們也省略了括號(hào),把整個(gè)方法的定義放到一行中。方法主體前面的等于號(hào)告訴編譯器函數(shù)的主體從這里開(kāi)始,就像我們看到的一樣。

我們也在字面函數(shù)里利用一些簡(jiǎn)寫(xiě)。之前我們像這樣寫(xiě)一個(gè)函數(shù):

 
 
 
  1. (s:String) => s.toUpperCase() 

我們可以簡(jiǎn)化成如下表達(dá)式:

 
 
 
  1. _.toUpperCase() 

因?yàn)閙ap 接受一個(gè)參數(shù),即一個(gè)函數(shù),我們可以用 _ 占位符來(lái)替代有名參數(shù)。也就是說(shuō),_ 像是一個(gè)匿名變量,在調(diào)用 toUpperCase 之前每一個(gè)字符串都會(huì)被賦值給它。注意,String 類(lèi)型是被推斷出來(lái)的。將來(lái)我們會(huì)看到,Scala 還會(huì)在某些上下文中充當(dāng)通配符。

你可以在一些更復(fù)雜的字面函數(shù)中使用這種簡(jiǎn)化的語(yǔ)法,就像我們將在《第3章 - 要點(diǎn)詳解》中看到的那樣。

在***一行,我們使用了一個(gè)object 而不是一個(gè)class 來(lái)簡(jiǎn)化代碼。我們只要在Upper object 上直接調(diào)用upper 方法,而不用new Upper 來(lái)創(chuàng)建一個(gè)新的實(shí)例。(注意,這樣的語(yǔ)法看起來(lái)很像在Java 類(lèi)中調(diào)用一個(gè)靜態(tài)方法。

***,Scala 自動(dòng)導(dǎo)入了許多用以輸入輸出的方法,比如println,所以我們不用寫(xiě)成Console.println()。我們只使用println 本身就可以了。(參見(jiàn)《第7章 - Scala Obejct 系統(tǒng)》的“預(yù)定義Object ”章節(jié)來(lái)獲取更多有關(guān)自動(dòng)導(dǎo)入類(lèi)型和方法的信息。)

讓我們來(lái)做***一次重構(gòu);讓我們把這段腳本變成一個(gè)編譯好的命令行工具。

 
 
 
  1. // code-examples/IntroducingScala/upper3.scala  
  2. object Upper {  
  3.   def main(args: Array[String]) = {  
  4.     args.map(_.toUpperCase()).foreach(printf("%s ",_))  
  5.     println("")  
  6.   }  

現(xiàn)在upper 方法被重命名為main。因?yàn)閁pper 是一個(gè)object,這個(gè)main 方法就像Java 類(lèi)里的static main 方法一樣。這個(gè)Upper 程序的入口。

注意

在Scala,main 必須是一個(gè)object 的函數(shù)。(在Java,main 必須是一個(gè)類(lèi)的靜態(tài)方法。)命令行參數(shù)會(huì)作為一個(gè)字符串?dāng)?shù)組被傳入應(yīng)用程序,比如 args: Array[String]。

main 方法的***行使用了和我們剛才產(chǎn)看過(guò)的map 方法一樣的簡(jiǎn)寫(xiě)。

 
 
 
  1. args.map(_.toUpperCase())... 

調(diào)用map 會(huì)返回一個(gè)新的集合。我們用foreach 來(lái)遍歷它。我們?cè)趥鹘oforeach 的這個(gè)字面函數(shù)中再一次使用了一個(gè) _ 占位符。這樣,集合的每一個(gè)字符串會(huì)被作為printf 的參數(shù)傳入。

 
 
 
  1. ...foreach(printf("%s ",_)) 

更清楚地說(shuō)明一下,這兩個(gè)“_”是完全相互獨(dú)立的。這個(gè)例子里的連鎖方法(Method Chaining)和簡(jiǎn)寫(xiě)字面函數(shù)需要花一些時(shí)間來(lái)習(xí)慣,但是一旦你熟悉了它們,他們用最少的臨時(shí)變量來(lái)產(chǎn)生可讀性很高的代碼。

main 的***一行在輸出中加入了一個(gè)換行。

在這次,你必須先用scalac 來(lái)把代碼編譯成JVM 可認(rèn)的.class 文件。

scalac upper3.scala

你現(xiàn)在應(yīng)該有一個(gè)名為Upper.class 的文件,就像你剛編譯了一個(gè)Java 類(lèi)一樣。

注意

你可能已經(jīng)注意到編譯器并沒(méi)有因?yàn)槲募麨閡pper3.scala 而object 名為Upper 而抱怨。不像Java,這里文件名不用和公開(kāi)域內(nèi)的類(lèi)型名字一致。(我們會(huì)在《第5章 - Scala 基礎(chǔ)面向?qū)ο缶幊獭返摹翱梢?jiàn)性規(guī)則”章節(jié)來(lái)探索可見(jiàn)性規(guī)則。)實(shí)際上,和Java 不同,你可以在一個(gè)單獨(dú)文件中有很多公開(kāi)類(lèi)型。此外,文件的地址也不用和包的聲明一致。不過(guò),如果你愿意,你可以依舊遵循Java 的規(guī)則。

現(xiàn)在,你可以傳入任意多個(gè)字符串來(lái)執(zhí)行這個(gè)命令了。比如:

 
 
 
  1. scala -cp . Upper Hello World! 

-cp 選項(xiàng)會(huì)把當(dāng)前目錄加入到“類(lèi)路徑”的搜索中去。你會(huì)得到如下輸出:

 
 
 
  1. HELLO WORLD! 

這樣,我們已經(jīng)滿足了一本編程語(yǔ)言書(shū)籍必須以一個(gè)“hello world ”程序開(kāi)始的基本要求。

#p#

初嘗并發(fā)

被Scala 吸引有很多原因。 其中一個(gè)就是Scala 庫(kù)的Actors API。它基于Erlang [Haller2007] 強(qiáng)大的Actors 并發(fā)模型建立。這里有一個(gè)例子來(lái)滿足你的好奇心。

在Actor 并發(fā)模型[Agha1987] 中, 被稱(chēng)為執(zhí)行者(Actor) 的獨(dú)立軟件實(shí)體不會(huì)互相之間共享狀態(tài)信息. 相反, 它們通過(guò)交換消息來(lái)通信. 沒(méi)有了共享易變狀態(tài)的需要, 就更容易寫(xiě)出健壯的并發(fā)應(yīng)用程序.

在這個(gè)例子里, 不同的圖形的實(shí)例被發(fā)送到執(zhí)行者(Actor )來(lái)進(jìn)行繪畫(huà)和顯示. 想象這樣一個(gè)場(chǎng)景: 一個(gè)渲染集群在為動(dòng)畫(huà)生成場(chǎng)景. 在場(chǎng)景渲染完成之后, 場(chǎng)景中的元圖形會(huì)被發(fā)送到一個(gè)執(zhí)行者中由顯示子系統(tǒng)處理.

我們從定義一系列的Shape (形狀) 類(lèi)開(kāi)始.

 
 
 
  1. // code-examples/IntroducingScala/shapes.scala  
  2. package shapes {  
  3.   class Point(val x: Double, val y: Double) {  
  4.     override def toString() = "Point(" + x + "," + y + ")"  
  5.   }  
  6.   abstract class Shape() {  
  7.     def draw(): Unit  
  8.   }  
  9.  class Circle(val center: Point, val radius: Double)   
  10.     extends Shape {  
  11.     def draw() = println("Circle.draw: " + this)  
  12.     override def toString() =   
  13.        "Circle(" + center + "," + radius + ")"  
  14.   }  
  15.   class Rectangle(val lowerLeft: Point, val height: Double, val width: Double)  
  16.         extends Shape {  
  17.     def draw() = println("Rectangle.draw: " + this)  
  18.     override def toString() =  
  19.       "Rectangle(" + lowerLeft + "," + height + "," + width + ")"  
  20.   }  
  21.   class Triangle(val point1: Point, val point2: Point, val point3: Point)  
  22.         extends Shape {  
  23.     def draw() = println("Triangle.draw: " + this)  
  24.     override def toString() =  
  25.       "Triangle(" + point1 + "," + point2 + "," + point3 + ")"  
  26.   }  

類(lèi)Shape 的繼承結(jié)構(gòu)在shapes 包(package)中定義。你可以用Java 的語(yǔ)法定義包,但是Scala 也支持類(lèi)似于C# 的名稱(chēng)空間的語(yǔ)法,就是把整個(gè)聲明都包含在大括號(hào)的域中,就像這里所做的。Java 風(fēng)格的包聲明語(yǔ)法并不經(jīng)常用到,然而,它們都一樣精簡(jiǎn)和可讀。

類(lèi)Point(點(diǎn))表示了在一個(gè)平面上的二位點(diǎn)。注意類(lèi)名字后面的參數(shù)列表。它們是構(gòu)造函數(shù)的參數(shù)。在Scala 中,整個(gè)類(lèi)的主體就是構(gòu)造函數(shù),所以你可以在類(lèi)名字后面,類(lèi)實(shí)體之前的主構(gòu)造函數(shù)里列出所有參數(shù)。(在《第5章 - Scala 的基本面向?qū)ο缶幊獭返摹癝cala 的構(gòu)造函數(shù)”章節(jié)中,我們會(huì)看到如何定義輔助的構(gòu)造函數(shù)。)因?yàn)槲覀冊(cè)诿恳粋€(gè)參數(shù)聲明前放置了val 關(guān)鍵字,它們會(huì)被自動(dòng)地轉(zhuǎn)換為有同樣名字的只讀的字段,并且伴有同樣名字的公開(kāi)讀取方法。也就是說(shuō),當(dāng)你初始化一個(gè)Point 的實(shí)例時(shí),比如point, 你可以通過(guò)point.x 和point.y 來(lái)讀取字段。如果你希望有可變的字段,那么使用var 關(guān)鍵字。我們會(huì)在《第2章 - 打更少的字,做更多的事》的“變量聲明”章節(jié)來(lái)探索如何使用val 和var 關(guān)鍵字聲明變量。

Point 類(lèi)的主體定義了一個(gè)方法,類(lèi)似于Java 的toString 方法的重寫(xiě)(或者C# 的ToString 方法)。主意,Scala 像C# 一樣,在重寫(xiě)一個(gè)具體方法時(shí)需要顯式的override 關(guān)鍵字。不過(guò)和C# 不一樣的是,你不需要一個(gè)virtual (虛擬)關(guān)鍵字在原來(lái)的具體方法上。實(shí)際上,在Scala 中沒(méi)有virtual 關(guān)鍵字。像之前一樣,我們省略了toString 方法主體兩邊的大括號(hào)“{…}”,因?yàn)槲覀冎挥幸粋€(gè)表達(dá)式。

Shape 是一個(gè)抽象類(lèi)。Scala 中的抽象類(lèi)和Java 以及C# 中的很像。我們不能實(shí)例化一個(gè)抽象類(lèi),即使它們的字段和方法都是具體的。

在這個(gè)例子里,Shape 聲明了一個(gè)抽象的draw (繪制)方法。我們說(shuō)它抽象是因?yàn)樗鼪](méi)有方法主體。在方法上不用寫(xiě)abstract (抽象)關(guān)鍵字。Scala 中的抽象方法就像Java 和C# 中的一樣。(參見(jiàn)《第6章 - Scala 高級(jí)面向?qū)ο缶幊獭返摹爸貙?xiě)Classes 和Traits 的成員”章節(jié)獲取更多細(xì)節(jié)。)

draw 方法返回Unit,這種類(lèi)型和Java 這樣的C 后繼語(yǔ)言中的void 大體一致。(參見(jiàn)《第7章 - Scala Object 系統(tǒng)》的“Scala 類(lèi)型組織”來(lái)獲取更多細(xì)節(jié)。)

Circle (圓)被聲明為Shape 的一個(gè)具體的子類(lèi)。 它定義了draw 方法來(lái)簡(jiǎn)單地打印一條消息到控制臺(tái)。Circle 也重寫(xiě)了toString。

Rectangle 也是Shape 得一個(gè)具體子類(lèi),定義了draw 方法,重寫(xiě)了toString。為了簡(jiǎn)單起見(jiàn),我們假設(shè)它不會(huì)相對(duì)X 或Y 軸旋轉(zhuǎn)。于是,我們所需要的就是一個(gè)點(diǎn),左下角的點(diǎn)就可以,以及長(zhǎng)方形的高度和寬度。

Triangle (三角形)遵循了同樣的模式。它獲取3個(gè)點(diǎn)作為它的構(gòu)造函數(shù)參數(shù)。

在Circle,Rectangle 和Triangle 的所有draw 方法里都用到了this。和Java,C# 一樣,this 是一個(gè)實(shí)例引用自己的方式。在這里的上下文中,this 在一個(gè)String 的鏈接表達(dá)式(使用加號(hào))的右邊,this.toString 被隱式地調(diào)用了。

注意

當(dāng)然,在一個(gè)真正的程序中,你不會(huì)像這樣實(shí)現(xiàn)一個(gè)域模型里的drawing 方法,因?yàn)閷?shí)現(xiàn)會(huì)依賴于操作系統(tǒng)平臺(tái),繪圖API 等細(xì)節(jié)。我們會(huì)在《第4章 - Traits》里看到一個(gè)更好地設(shè)計(jì)方式。

既然我們已經(jīng)定義了我們的形狀類(lèi)型,讓我們回過(guò)頭來(lái)看Actors。我們定義了一個(gè)Actor 來(lái)接受消息(需要繪制的Shape)。

 
 
 
  1. // code-examples/IntroducingScala/shapes-actor.scala  
  2. package shapes {  
  3.   import scala.actors._  
  4.   import scala.actors.Actor._  
  5.   object ShapeDrawingActor extends Actor {  
  6.     def act() {  
  7.       loop {  
  8.         receive {  
  9.           case s: Shape => s.draw()  
  10.           case "exit"   => println("exiting..."); exit  
  11.           case x: Any   => println("Error: Unknown message! " + x)  
  12.         }  
  13.       }  
  14.     }  
  15.   }  

Actor 被聲明為shapes 包的一部分。接著,我們有兩個(gè)import (導(dǎo)入)表達(dá)式。

***個(gè)import 表達(dá)式導(dǎo)入了所有在scala.actors 包里的類(lèi)型。在Scala 中,下劃線_ 的用法和Java 中的星號(hào)* 的用法一致。

注意

因?yàn)? 是方法名允許的合法字符,它不能在import 被用作通配符。所以,_ 被保留來(lái)作為替代。

Actor 的所有方法和公開(kāi)域內(nèi)的字段會(huì)被導(dǎo)入。Actor 類(lèi)型中沒(méi)有靜態(tài)導(dǎo)入類(lèi)型,雖然Java 中會(huì)。不過(guò),它們會(huì)被導(dǎo)入為一個(gè)object,名字一樣為Actor。類(lèi)和object 可以使用同樣的名字,就像我們會(huì)在《第6章 - Scala 高級(jí)面向?qū)ο缶幊獭返摹鞍殡S實(shí)體”章節(jié)中看到的那樣。

我們的Actor 類(lèi)定義,ShapeDrawingActor,是繼承自Actor (類(lèi)型,不是實(shí)體)的一個(gè)實(shí)體。它的act 方法被重寫(xiě)來(lái)執(zhí)行Actor 的實(shí)際工作。因?yàn)閍ct 是一個(gè)抽象方法,我們不需要顯式地用override 關(guān)鍵字來(lái)重寫(xiě)。我們的Actor 會(huì)無(wú)限循環(huán)來(lái)等待進(jìn)來(lái)的消息。

在每一次循環(huán)中,receive 方法會(huì)被調(diào)用。它會(huì)阻塞當(dāng)前線程直到一個(gè)新的消息到來(lái)。為什么在receive 后面的代碼被包含在大括號(hào){}中而不是小括號(hào)()呢?我們會(huì)在后面學(xué)到,有些情況下這樣的替代是被允許的,而且十分有用(參見(jiàn)《第3章 - Scala 本質(zhì)》)?,F(xiàn)在,我們需要知道的是,在括號(hào)中的表達(dá)式組成了一個(gè)字面函數(shù),并且傳遞給了receive。這個(gè)字面函數(shù)給消息做了一個(gè)模式匹配來(lái)決定它被如何處理。由于case 語(yǔ)句的存在,它看上去像Java 中的一個(gè)典型的switch 表達(dá)式,實(shí)際上它們的行為也很相像。

***個(gè)case 給消息做了一個(gè)類(lèi)型比較。(在代碼中沒(méi)有為消息實(shí)體做顯式變量聲明;它是被推斷出來(lái)的。)如果消息是Shape 類(lèi)型的,***個(gè)case 會(huì)被滿足。消息實(shí)體會(huì)被轉(zhuǎn)換成Shape 并且賦值給變量s,然后s 的draw 方法會(huì)被調(diào)用。

如果消息不是一個(gè)Shape,第二個(gè)case 會(huì)被嘗試。如果消息是字符串 exit ,Actor 會(huì)打印一條消息然后結(jié)束執(zhí)行。Actors 通常需要一個(gè)優(yōu)雅退出的方式。

***一個(gè)case 處理所有其它任何類(lèi)型的消息實(shí)例,作用和default (默認(rèn))case 一樣。Actor 會(huì)報(bào)告一個(gè)錯(cuò)誤然后丟棄這個(gè)消息。Any 是Scala 類(lèi)型結(jié)構(gòu)中所有類(lèi)型的父類(lèi)型,就像Java 和其他類(lèi)型語(yǔ)言中的Object 根類(lèi)型一樣。所以,這個(gè)case 塊會(huì)匹配任何類(lèi)型的消息。模式匹配是頭饑餓的怪獸,我們必須把這個(gè)case 塊放在***,這樣它才不會(huì)把我們需要的消息也都吃掉!

回想一樣我們?cè)赟hape 類(lèi)里定義draw 為一個(gè)抽象方法,然后我們?cè)诰唧w的子類(lèi)里實(shí)現(xiàn)它。所以,在***個(gè)case 塊中的代碼執(zhí)行了一個(gè)多態(tài)操作。

模式匹配 vs. 多態(tài)

模式匹配在函數(shù)式編程中扮演了中心角色, 就好像多態(tài)在面向?qū)ο缶幊讨邪缪葜行慕巧粯印:瘮?shù)式的模式匹配比絕大多數(shù)像Java 這樣的命令式語(yǔ)言中的switch/case 語(yǔ)句更加重要和成熟。我們會(huì)在《第8章 - Scala 函數(shù)式編程》了解更多Scala 對(duì)于模式匹配支持的細(xì)節(jié)。在我們的這個(gè)例子里,我們可以開(kāi)始看到,函數(shù)式模式匹配和面向?qū)ο蠖鄳B(tài)調(diào)度的有力結(jié)合會(huì)給Scala 這樣的混合范式語(yǔ)言帶來(lái)巨大好處。

***,這里有一段腳本來(lái)使用ShapeDrawingActor。

 
 
 
  1. // code-examples/IntroducingScala/shapes-actor-script.scala  
  2. import shapes._  
  3. ShapeDrawingActor.start()  
  4. ShapeDrawingActor ! new Circle(new Point(0.0,0.0), 1.0)  
  5. ShapeDrawingActor ! new Rectangle(new Point(0.0,0.0), 2, 5)  
  6. ShapeDrawingActor ! new Triangle(new Point(0.0,0.0),  
  7.                                  new Point(1.0,0.0),  
  8.                                  new Point(0.0,1.0))  
  9. ShapeDrawingActor ! 3.14159  
  10. ShapeDrawingActor ! "exit" 

在shapes 包里的所有形狀類(lèi)會(huì)被導(dǎo)入。

ShapeDrawingActor 會(huì)被啟動(dòng)。默認(rèn)情況下,它會(huì)運(yùn)行在它自己的線程中(也有另外的選擇,我們會(huì)在《第9章 - 使用Actor 的健壯的,可伸縮的并發(fā)編程》中討論),等待消息。

有5個(gè)消息通過(guò)使用語(yǔ)法 actor ! message 被送到Actor。***個(gè)消息發(fā)送了一個(gè)Circle 實(shí)例。Actor 會(huì)“畫(huà)”出這個(gè)圓。第二個(gè)消息發(fā)送了Rectangle 消息。Actor 會(huì)“畫(huà)”出這個(gè)長(zhǎng)方形。第三個(gè)消息對(duì)一個(gè)三角形做了同樣的事情。第四個(gè)消息發(fā)送了一個(gè)約等于Pi 的Double (雙精度浮點(diǎn)數(shù))值。這對(duì)于Actor 來(lái)說(shuō)是一個(gè)未知消息,所以它只是打印了一個(gè)錯(cuò)誤消息。***一個(gè)消息發(fā)送了exit 字符串,它會(huì)導(dǎo)致Actor 退出。

要實(shí)驗(yàn)這個(gè)Actor 例子,從編譯這兩個(gè)源文件開(kāi)始。你可以從O'Reilly 下載網(wǎng)站獲取源代碼(參見(jiàn)前言中獲取代碼示例的部分來(lái)取得更多細(xì)節(jié)信息),或者你也可以自己創(chuàng)建它們。

使用下面的命令來(lái)編譯文件。

 
 
 
  1.   scalac shapes.scala shapes-actor.scala 

雖然源文件的名字和位置并不和文件內(nèi)容匹配,你會(huì)發(fā)現(xiàn)生成的class 文件被寫(xiě)入到一個(gè)shape 文件夾內(nèi),每一個(gè)類(lèi)都會(huì)有一個(gè)class 文件對(duì)應(yīng)。這些class 文件的名字和位置必須和JVM 的需求相吻合。

現(xiàn)在你可以運(yùn)行這個(gè)腳本來(lái)看看Actor 的實(shí)際運(yùn)行。

 
 
 
  1.   scala -cp . shapes-actor-script.scala 

你應(yīng)該可以看到如下輸出。

 
 
 
  1.   Circle.draw: Circle(Point(0.0,0.0),1.0)  
  2.   Rectangle.draw: Rectangle(Point(0.0,0.0),2.0,5.0)  
  3.   Triangle.draw: Triangle(Point(0.0,0.0),Point(1.0,0.0),Point(0.0,1.0))  
  4.   Error: Unknown message! 3.14159  
  5.   exiting... 

要知道更多關(guān)于Actor 的細(xì)節(jié),參加《第9章 - 使用Actor 的強(qiáng)壯的,可伸縮的并發(fā)編程》。

概括

我們通過(guò)Scala 的示例來(lái)讓你開(kāi)始對(duì)Scala 有所了解,其中一個(gè)還給出了Scala Actors 庫(kù)的強(qiáng)大并發(fā)編程體驗(yàn)。下面,我們會(huì)更深入Scala 語(yǔ)法,強(qiáng)調(diào)各種各樣快速完成大量任務(wù)的“鍵盤(pán)金融”方式。


文章名稱(chēng):Scala語(yǔ)言編程入門(mén)指南
本文網(wǎng)址:http://www.5511xx.com/article/cdeoose.html