Scala講座:類定義和構造函數
本文節(jié)選自最近在日本十分流行的Scala講座系列的第三篇,由JavaEye的fineqtbull翻譯。本系列的作者牛尾剛在日本寫過不少有關Java和Ruby的書籍,相當受歡迎。
序言
到這為止牛尾先生作了一下Scala語言的介紹,接下來以微型旅游的形式做一下有關Scala語法特點的探險。如果是初次接觸的讀者可以大略的讀一下第一和第二回的講座,就可以順暢地讀懂本文了。
這次旅行準備訪問的是,類定義和構造函數;混合Scala與Java程序以及與Java語言在語法上的差別;稱為特征(Trait)的mixin型多重繼承;函數定義和函數式編程;類型層次和集合;模式匹配;XML文本操作;等主題。
首先,作為復習我們先整理一下Scala語言的要點。
• 事實1:Scala中可以簡單使用所有Java類,Java中也可以自由調用Scala類。Scala類還可以定義為Java類的子類。也就是說,龐大的已有Java、J2EE、Java ME和CLDC資源可以被更有效和合理地應用了。(在Net上,雖然現在的版本還不支持,1.x版曾經支持過,將來也有復活的計劃)
• 事實2:Scala在JVM上被執(zhí)行,編譯后的代碼執(zhí)行性能基本與Java代碼不相上下。結果是比大多數腳本語言的速度都快一位數以上。
• 事實3:Scala一方面是純面向對象的語言,另一方面在這框架中提供了完全的函數式編程的功能。Scala的所有數據都是對象,函數也是對象,可以作為數據來操作。
• 事實4:Scala在對于開發(fā)Javac和Java Generics有貢獻的Martin Ordersky教授所率領的強大的開放體制下被開發(fā),不是實驗室的實驗品,而是將來可以期待的通用編程語言。Scala發(fā)布的頻率非???,文檔也很豐富,現在的版本是Scala2.7.1 final(2008/8)。
類定義和構造函數
那么,定義一下類吧。首先定義一下在語言介紹中一直用到的Person類。首先是Java類
- public class Person
- {
- private String lastName; //姓
- private String firstName; //名
- private Person spouse; //配偶的Person對象
- public Person(String fn, String ln, Person s)
- {
- lastName = ln; firstName = fn; spouse = s;
- }
- public Person(String fn, String ln)
- {
- this(fn, ln, null); //未婚時沒有配偶
- }
- public String getFirstName()
- {
- return firstName;
- }
- public String getLastName()
- {
- return lastName;
- }
- public Person getSpouse()
- {
- return spouse;
- }
- public void setSpouse(Person p)
- {
- spouse = p;
- //沒有考慮婚姻對姓和名的影響
- }
- public String introduction()
- {
- return "我的名字是," + firstName + " " + lastName +
- (spouse != null ?
- " 對方的名字是," + spouse.firstName + " " + spouse.lastName + " 。" :
- " 。");
- }
- }
下面是用Scala寫的同樣內容
- class Person(fn : String, ln : String, s : Person)
- {
- val lastName = ln; //沒有private修飾符則認為是public
- val firstName = fn; //從構造函數的參數類型推斷為String
- var spouse = s; //從構造函數的參數類型推斷為Person
- def this(fn : String, ln : String) = { this(fn, ln, null); }
- def introduction() : String =
- return "我的名字是, " + lastName + " " + firstName +
- (if (spouse != null) " 對方的名字是, " + spouse.lastName + " " + spouse.firstName + "。" else "。");
- }
從行數來看大概縮短為1/3,代碼變得非常簡潔了。用val來定義常量,var來定義可再賦值的實例屬性。用def來定義方法。Scala的目的之一就是使書寫的代碼更簡潔易讀。
在Scala中實例屬性默認為public,可以用該實例屬性名來直接存取屬性的值。
- scala> val p0 = new Person("Fei", "Zhang")
- p0: Person = Person@6e9b6a
- scala> p0.introduction
- res1: String = 我的名字是, Zhang Fei。
而且Scala對于調用方法的“.”符號,在不發(fā)生歧義的情況下可以替換為空格。但是,這里不能加上空的參數表()。
- scala> p0 firstName //同p0.firstName等同
- res3: String = Fei
- scala> p0 spouse //現在未婚,所以spouse的值為null
- res4: Person = null
- scala> p0.spouse() //注意,Scala中.m和.m()的含義不同
: 6: error: p0.spouse of type Person does not take parameters- p0.spouse()
接下來那讓ZhangFei結婚吧。先準備好女方DiaoChan對象,然后把她設置到p0的spouse屬性。這時構造函數第三個參數為配偶,所以賦予p0。
為了防止誤解先說明一下,p0最初是用val來定義的所以是不可再賦值的,不過改變p0所指對象的內部狀態(tài)還是可以的。因此,如下所示結婚后還可以讓他再婚。
- scala> p0 spouse = new Person("Chan", "Diao", p0) //DiaoChan和ZhangFei結婚
- scala> (p0 spouse) firstName //寫成p0 spouse firstName就會出錯
- res6: String = Chan
- scala> p0 spouse = null //ZhangFei離婚
- scala> p0 spouse = new Person("Shi", "Xi", p0) //和XiShi再婚
定義Scala的類比較有趣的是定義基本(primary)構造函數時在類名稱后直接加上構造函數的參數表。基本構造函數參數的類型不可省略(包括模式匹配,這是對象的類型信息的基礎,不能省略也是當然的)。另一方面,可以注意到類型定義中的變量定義沒有指定類型,這都是靠從構造函數參數的類型推斷出來的。
而且聲明為def this(ln:String, fn:String)的派生構造函數也是從基本構造函數而來的。函數體內通過調用this(ln, fn, null),給基本構造函數的第三個參數賦予null來實現為了未婚人士準備的只有兩個參數的構造函數。
這個類定義還可以縮短如下
- class Person(val firstName:String, val lastName:String, var spouse:Person) {
- def this(fn:String, ln:String) = this(fn, ln, null)
- def introduction = "我的名字是," + lastName + " " + firstName +
- (if (spouse != null) ",對方的名字是," + spouse.lastName + " " + spouse.firstName + "。" else "。")
- }
由于在類中基本構造函數的參數定義前加上val或var后,對應的實例屬性就會被定義,所以原來的屬性定義就不需要了。而且,編譯器會自動追加用于存取這些屬性的方法。Scala中基本上可以去除語句尾部的“;”符號,這里也都去除了。
- scala> val p1 = new Person("Yu", "Guan", new Person("ZheTian", "Wu"))
- p1: Person = Person@904f75
- scala> p1.lastName
- res7: String = Guan
- scala> p1.spouse
- res8: Person = Person@2e879
上述程序中描述對象的字符串比較難解,下面就擴展一下類使他顯示姓和名吧。任何對象的文字描述是由對象的最根類Any的toString:String方法來實現的。在各個類中使用自己的實現來覆蓋這個方法就可以了,這里的顯示格式為[姓:firstName 名:lastName 配偶:沒有或(姓:firstName 名:lastName)]。另外,Scala中覆蓋父類的方法一定要加上override修飾符。
- class Person ... { ...
- override def toString : String = super.toString + " [姓: " + lastName + " 名: " + firstName + " 配偶: " + (if (spouse != null) " ("+ spouse.lastName + "," + spouse.firstName + ")" else "沒有") + "]"
- ...}
下面是修改后的效果
- scala> val p1 = new Person("Yu", "Guan", new Person("ZheTian", "Wu"))
- p1: Person = Person@4a0ac5 [姓: Guan 名: Yu 配偶: (Wu, ZheTian)]
- scala> p1
- res0: Person = Person@4a0ac5 [姓: Guan 名: Yu 配偶: (Wu, ZheTian)]
Scala講座中類定義和構造函數的內容就到這里。
【編輯推薦】