Scala:重載方法和字段及定義參數化字段
重載方法和字段
統一訪問原則只是Scala在對待字段和方法方面比Java更統一的一個方面。另一個差異是Scala里,字段和方法屬于相同的命名空間。這使得字段重載無參數方法成為可能。比如說,你可以改變類ArrayElement中contents的實現,從一個方法變為一個字段,而無需修改類Element中contents的抽象方法定義,如展示在代碼10.4中的那樣:
- class ArrayElement(conts: Array[String]) extends Element {
- val contents: Array[String] = conts
- }
代碼 10.4 用字段重載無參數方法
51CTO編輯推薦:Scala編程語言專題
這個ArrayElement的版本里,字段contents(用val定義)完美地實現了類Element里的無參數方法contents(用def定義)。
另一方面,Scala里禁止在同一個類里用同樣的名稱定義字段和方法,而在Java里這樣做被允許。例如,下面的Java類能夠很好地編譯:
但是相應的Scala類將不能編譯:
- // 在Java里的代碼
- class CompilesFine {
- private int f = 0;
- public int f() {
- return 1;
- }
- }
- class WontCompile {
- private var f = 0 // 編譯不過,因為字段和方法重名
- def f = 1
- }
通常情況下,Scala僅為定義準備了兩個命名空間,而Java有四個。Java的四個命名空間是字段,方法,類型和包。與之相對,Scala的兩個命名空間是:
值(字段,方法,包還有單例對象)
類型(類和特質名)
Scala把字段和方法放進同一個命名空間的理由很清楚,因為這樣你就可以使用val重載無參數的方法,這種你在Java里做不到的事情。Scala里包共享了與字段和方法相同的命名空間的原因是為了讓你能除了僅僅引用類型名以及單例對象的字段和方法之外,還能直接引用包。這同樣是你在Java中無法做到的。
定義參數化字段
再次考慮上一節中展示的ArrayElement類的定義。它有一個參數conts,其唯一目的是被復制到contents字段。選擇conts這個參數的名稱只是為了讓它看上去更像字段名contents而不會與它發生實際沖突。這是一種“代碼味道”,一個表明或許某些不必須的榮譽和重復在你代碼中出現的信號。
你可以通過在單一的參數化字段:parametric field定義中組合參數和字段避免這種代碼味道,展示在代碼10.5中:
- class ArrayElement( // 請注意,小括號
- val contents: Array[String]
- ) extends Element
代碼 10.5 定義contents為參數化字段
注意現在contents參數前綴了val。這是在同一時間使用相同的名稱定義參數和字段的一個簡寫方式。尤其特別的是,類ArrayElement現在擁有一個可以從類外部訪問的,(不能重新賦值的)字段contents。字段使用參數值初始化。就好象類被寫成如下的方式,其中x123是參數的任意未曾用過的名字:
- class ArrayElement(x123: Array[String]) extends Element {
- val contents: Array[String] = x123
- }
同樣也可以使用var前綴類參數,這種情況下相應的字段將能重新被賦值。最終,還有可能添加如private,protected,protected修飾符,可以授權給子類訪問,將在第13章詳細描述。或override這類的修飾符到這些參數化字段上,就好象你可以在其他類成員上做的事情。比方說,考察下列類定義:
- class Cat {
- val dangerous = false
- }
- class Tiger(
- override val dangerous: Boolean,
- private var age: Int
- ) extends Cat
Tiger的定義是以下包括重載成員dangerous和private成員age的類定義替代寫法的簡寫:
- class Tiger(param1: Boolean, param2: Int) extends Cat {
- override val dangerous = param1
- private var age = param2
- }
兩個成員都初始化自相應的參數。我們任意選擇了這些參數名,param1和param2。重要的是它們不會與范圍內的任何其它名稱沖突。
【相關閱讀】