前言
scala是以實現scaleable language為初衷設計出來的一門語言。官方中,稱它是object-oriented language和functional language的混合式語言。並且,scala可以和java程序無縫拼接,因為scala文件編譯后也是成為.class文件,並且在JVM上運行。不過,我更關心的是它的scaleable(擴展性)。一門語言到底怎樣才算有擴展性呢?對語言而言什么是它的擴展性呢?
個人拙見,認為語言的擴展性可能包括兩個方面:
1、語言本身的擴展性
2、由此語言寫出來的程序的擴展性
對於第一點,在scala的介紹中提到有些developer可以用scala來定義自己的Domain Specific Language。我想,這一點也許就體現了scala語言本身的擴展性,即它有潛力可以被加工為另一門可用在專門領域中的語言(或許是通過增加某些模型,或某些操作等)。
對於第二點,感觸最深的就是馬上要見到的Tuple。曾經在做一個項目時,希望一個函數可以多返回兩個參數,而不得不重新定義一個JavaBean(繼承了它的前輩以保留之前的其他返回值),但是這樣導致上層代碼一片混亂,一會兒是調用原先的BaseBean,一會兒又是調用新的Bean。如果Java中也能有類似Tuple的東西就太方便了。
文中將描述三個內容:
Tuple -可以將不同類型的數據存儲在一個數組中
singleton objects - scala中沒有靜態方法和屬性,全部由singleton object(單例對象)來替代
trait - scala中的類interface物,但是可以擁有方法體,並且可以在類實例化時混入,而不需為了包裝某個類而產生子類
Tuples
Like Lists, tuples are immutable, but unlike Lists, tuples can contain different types of elements. Thus whereas a list might be a List[Int] or a List[String], a tuple could contain both an Int and a String at the same time. Tuples are very useful, for example, if you need to return multiple objects from a method. Whereas in Java, you would often create a JavaBean-like class to hold the multiple return values, in Scala you can simply return a tuple.
val pair = (99, "Luftballons")
println(pair._1)
println(pair._2)
輸出:
99
Luftballons
Understand classes and singleton objects
// In greetSimply.scala class SimpleGreeter { val greeting = "Hello, world!" def greet() = println(greeting) } val g = new SimpleGreeter g.greet()
as in Java, classes in Scala encapsulate fields and methods. Fields are defined with either val or var. Methods are defined with def.
Although classes in Scala are in many ways similar to Java, in several ways they are quite different.
① One difference between Java and Scala involves constructors.
In Java, classes have constructors, which can take parameters, whereas in Scala, classes can take parameters directly. The Scala notation is more concise—class parameters can be used directly in the body of the class; there’s no need to define fields and write assignments that copy constructor parameters into fields.
// In greetFancily.scala class FancyGreeter(greeting: String) { def greet() = println(greeting) } val g = new FancyGreeter("Salutations, world") g.greet
但是這樣寫的一個特點是greeting參數是常量,不能在FancyGreeter內部重新賦值。
② Another area in which Scala departs from Java is that you can't have any static fields or methods in a Scala class.
Instead, Scala allows you to create singleton objects using the keyword object. A singleton object cannot, and need not, be instantiated with new. It is essentially automatically instantiated the first time it is used, and as the “singleton” in its name implies, there is ever only one instance. A singleton object can share the same name with a class, and when it does, the singleton is called the class's companion object. The Scala compiler transforms the fields and methods of a singleton object to static fields and methods of the resulting binary Java class.
// In WorldlyGreeter.scala // The WorldlyGreeter class class WorldlyGreeter(greeting: String) { def greet() = { val worldlyGreeting = WorldlyGreeter.worldify(greeting) println(worldlyGreeting) } } // The WorldlyGreeter companion object object WorldlyGreeter { def worldify(s: String) = s + ", world!" }
如果沒有相對應的class,而是只有一個object的話,則稱為stand-alone. object
// In WorldlyApp.scala // A singleton object with a main method that allows // this singleton object to be run as an application object WorldlyApp { def main(args: Array[String]) { val wg = new WorldlyGreeter("Hello") wg.greet() } }
a singleton object is either a companion or a stand-alone object.
③ java文件必須要以文件內的class為名,但是scala卻不需要。
不過盡管如此,編者還是希望使用class名為scala文件名,因為這樣更便於其他程序員查找所需的文件。
由此,scala文件將分為兩種,一種是class的,一種是script的。Class型的文件是不能直接運行的,script的可以。Script語句必須以執行語句作為文件的結尾。
scala WorldlyGreeter.scala # This won't work!
WorldlyGreeter.scala文件必須經由WorldlyApp.scala文件才能帶動起來,因而需要將這兩個文件編譯到一起。可以用scalac指令來完成這一任務:
scalac WorldlyApp.scala WorldlyGreeter.scala
由於每次執行scalac指令的時候,都會產生一個JVM的實例。而JVM在啟動時會有明顯的能讓人察覺的延遲,因此可以考慮使用fsc指令來代替scalac指令。第一次執行fsc指令時,它會產生一個守護進程,並且將此進程與本機的一個端口號綁定。所有需要編譯的文件將通過這個端口號傳給守護進程。這個進程不會停止直到你使用指令fsc - shutdown人為關閉為止。這樣,除了在第一次使用時會有延遲外,其他時候都會馬上編譯給定的文件而不需要再次啟動JVM。
Understand traits and mixins
Trait有點類似於java中的interface,但也有不同之處。
①java的interface只定義方法名稱和參數列表,不能定義方法體。而trait則可以定義方法體。
如
trait Friendly { def greet() = "Hi" }
②在java中實現接口用implement,而在scala中,實現trait用extends。
Scala中不再有implement這個關鍵詞。但類似的,scala中的class可以繼承0至多個traits。
class Dog extends Friendly { override def greet() = "Woof" }
此處,需要注意的一點是,與java不同,在scala中重寫一個方法是需要指定override關鍵詞的。如果重寫一個方法時,沒有加上override關鍵詞,那么scala編譯會無法通過。
③ java的interface和scala的trait的最大區別是,scala可以在一個class實例化的時候混合進一個trait。
trait Friendly { def greet() = "Hi" } class Dog extends Friendly { override def greet() = "Woof" } class HungryDog extends Dog { override def greet() = "I'd like to eat my own dog food" } trait ExclamatoryGreeter extends Friendly { override def greet() = super.greet() + "!" } var pet: Friendly = new Dog println(pet.greet()) pet = new HungryDog println(pet.greet()) pet = new Dog with ExclamatoryGreeter println(pet.greet()) pet = new HungryDog with ExclamatoryGreeter println(pet.greet())
輸出為:
Woof
I'd like to eat my own dog food
Woof!
I'd like to eat my own dog food!
這樣做的好處是什么呢?
從前,如果想要輸出在HungryDog 類的“I'd like to eat my own dog food”輸出的基礎上變成“I'd like to eat my own dog food!”,那么需要再增加一個子類,並重寫它的方法,給輸出字串上加上!,而在trait中,只需要在HungryDog 類實例化的時候“with”上一個trait,就可以達到包裝的效果了。
所以,with關鍵字可以用來實現包裝器的功能。
Reference: