歡迎您光臨本站 註冊首頁

Scala編程指南:面向對象編程(2)

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

為了方便,我們會使用通用的「類型」 這一詞語來指代類和Trait,對應的還有成員類型.除非特別聲明,否則我們在使用通用術語「成員」 時會包含這些定義.
大多數面向對象語言都有控制類型或者類型成員可見性(作用域)聲明的結構.這些結構支持面向對象式的封裝,即本質上只有類或者Trait 的公共抽象會被暴露出來,內部實現則被隱藏於視界之下.
對於你的類、對象的用戶所希望看到和使用的任何地方你都會想用公共可見性.但是記住,公共可見成員的集合構成了類型暴露出的抽象介面,包括類型的名字.
面向對象設計世界的傳統智慧是,欄位應該為私有(private)或者受保護的(protected).如果有存取需求,也應該通過方法來完成,而不是是的所有東西都默認可存取.統一訪問原則(參見章節「當存取方法和欄位無法區分時:統一訪問原則」)歸根結底是說我們可以通過方法或欄位的直接存取給予用戶公共(public)欄位的訪問語意,只要它對於任務來說是合適的即可.
提示
好的面向對象設計的藝術在於定義最小的,清晰的,有凝聚力的公共抽象層.
類型有兩種「用戶」: 繼承類型,以及使用類型實例的代碼.繼承類型通常比實例用戶需要更多地存取父類型的成員.
Scala 的可見性規則和Java 類似,但是傾向於更統一和靈活.例如,在Java 中,如果一個內部類有一個私有成員,則包含它的外部類是能看到的.在Scala 里,則不能看到,但是Scala 提供了另外一種方式來聲明它對於包含它的外部類可見.
和Java,C# 一樣,修改可見性的關鍵字,比如private 和protected,在聲明的最開始出現.你會在class,trait 關鍵字前,val 或者var 前,以及方法的def 前發現它們.
注意
你也可以在類的主構造函數前使用一個可見性修飾符.如果有,把它放在類型名稱和類型參數后,參數列表之前.像這樣:
class Restricted[ A] private (name: String) {…} 表格 5.1,「可見域」 總結了可見性的範圍.
表格 5.1,可見域 名稱 關鍵字 描述
public 沒有 public 成員在任何地方都可見,跨越所有邊界
protected protected protected 成員對於定義它的類型,繼承類型以及嵌套類型可見,protected 類型僅在同一個包,子包中可見.
private private private 成員對於定義它的類型和嵌套類型可見,private 類型僅在同一個包可見.
scoped protected protected[scoped] 可見性被限制在域scoped 中,它可以是包,類型,或者this(對於成員來說就是該實例,對於類型來說就是它存在的包.參見下面的文字獲取更多信息.


scoped private private[scoped] 和scoped protected 一樣,除了繼承的時候.(下面會討論)
讓我們來仔細探索一下這些可見性選項.為了簡單,我們會使用欄位來作為成員的例子.方法,類型聲明的行為和欄位是一致的.
注意
不幸的是,你不能對包做任何可見性修飾.因此,一個包永遠都是public,即使它沒有包含任何public 類型.
Public 可見性
任何沒有顯式可見性關鍵字的聲明都是「public」,意味著它在任何地方都可見.在Scala 里沒有public 關鍵字.這和Java 恰恰相反,Java 的默認行為是只在當前包里默認是public 可見性(也就是包私有的-「package private」).其它面向對象語言,比如Ruby,也是默認public 可見性.
// code-examples/BasicOOP/scoping/public.scala package scopeA { class PublicClass1 { val publicField = 1 class Nested { val nestedField = 1 } val nested = new Nested } class PublicClass2 extends PublicClass1 { val field2 = publicField 1 val nField2 = new Nested().nestedField } } package scopeB { class PublicClass1B extends scopeA.PublicClass1 class UsingClass(val publicClass: scopeA.PublicClass1) { def method = "UsingClass:" " field: " publicClass.publicField " nested field: " publicClass.nested.nestedField } } 你可以用scalac 編譯這個文件,應該不會遇到編譯錯誤.
這些包和類的任何成員都是public 的.主意,scopeB.UsingClass 可以訪問scopeA.PublicClass1 和它的成員,包括嵌套類的實例以及它的public 欄位.
Protected 可見性
Protected 可見性為實現繼承的類型提供了一些好處,它需要對其父類型有更多的一些存取許可權.任何用protected 關鍵字聲明的成員只對定義它的類型,包括其實例和任何繼承類型可見.當應用於類型時,protected 限制其可見性於包含它的package 中.
J對比之下,Java 是的protected 成員對於整個包都可見.Scala 則用scoped (區域的)private 和protected 來控制這樣的情況.
// code-examples/BasicOOP/scoping/protected-wont-compile.scala // WON'T COMPILE package scopeA { class ProtectedClass1(protected val protectedField1: Int) { protected val protectedField2 = 1 def equalFields(other: ProtectedClass1) = (protectedField1 == other.protectedField1) && (protectedField1 == other.protectedField1) && (nested == other.nested) class Nested { protected val nestedField = 1 } protected val nested = new Nested } class ProtectedClass2 extends ProtectedClass1(1) { val field1 = protectedField1 val field2 = protectedField2 val nField = new Nested().nestedField // ERROR } class ProtectedClass3 { val protectedClass1 = new ProtectedClass1(1) val protectedField1 = protectedClass1.protectedField1 // ERROR val protectedField2 = protectedClass1.protectedField2 // ERROR val protectedNField = protectedClass1.nested.nestedField // ERROR } protected class ProtectedClass4 class ProtectedClass5 extends ProtectedClass4 protected class ProtectedClass6 extends ProtectedClass4 } package scopeB { class ProtectedClass4B extends scopeA.ProtectedClass4 // ERROR } 當你用scalac 編譯這個文件的時候,你會得到下列輸出.(為了配合排版,在N: 行號之前的文件名已經被移除.)


16: error: value nestedField cannot be accessed in ProtectedClass2.this.Nested val nField = new Nested().nestedField ^ 20: error: value protectedField1 cannot be accessed in scopeA.ProtectedClass1 val protectedField1 = protectedClass1.protectedField1 ^ 21: error: value protectedField2 cannot be accessed in scopeA.ProtectedClass1 val protectedField2 = protectedClass1.protectedField2 ^ 22: error: value nested cannot be accessed in scopeA.ProtectedClass1 val protectedNField = protectedClass1.nested.nestedField ^ 32: error: class ProtectedClass4 cannot be accessed in package scopeA class ProtectedClass4B extends scopeA.ProtectedClass4 ^ 5 errors found 列表中的//ERROR 註釋標識了無法解析的行.
ProtectedClass2 可以存取ProtectedClass1 的protected 成員,它們是繼承關係.然而,它不能存取protectedClass1.nested 的protected nestedField 欄位.,ProtectedClass3 不能存取它使用的ProtectedClass1 實例的protected 成員.
最終,ProtectedClass4 被聲明為protected,它對於scopeB 包來說不可見.
Private 可見性
Private 可見性完全隱藏了實現的細節,即使對於繼承類的實現也一樣.任何用private 關鍵字聲明的成員只對定義它的類型可見,包括它的實例.當應用於類型時,private 限制其可見性為包含它的package.
// code-examples/BasicOOP/scoping/private-wont-compile.scala // WON'T COMPILE package scopeA { class PrivateClass1(private val privateField1: Int) { private val privateField2 = 1 def equalFields(other: PrivateClass1) = (privateField1 == other.privateField1) && (privateField2 == other.privateField2) && (nested == other.nested) class Nested { private val nestedField = 1 } private val nested = new Nested } class PrivateClass2 extends PrivateClass1(1) { val field1 = privateField1 // ERROR val field2 = privateField2 // ERROR val nField = new Nested().nestedField // ERROR } class PrivateClass3 { val privateClass1 = new PrivateClass1(1) val privateField1 = privateClass1.privateField1 // ERROR val privateField2 = privateClass1.privateField2 // ERROR val privateNField = privateClass1.nested.nestedField // ERROR } private class PrivateClass4 class PrivateClass5 extends PrivateClass4 // ERROR protected class PrivateClass6 extends PrivateClass4 // ERROR private class PrivateClass7 extends PrivateClass4 } package scopeB { class PrivateClass4B extends scopeA.PrivateClass4 // ERROR } 編譯這個文件會產生如下輸出.
14: error: not found: value privateField1 val field1 = privateField1 ^ 15: error: not found: value privateField2 val field2 = privateField2 ^ 16: error: value nestedField cannot be accessed in PrivateClass2.this.Nested val nField = new Nested().nestedField ^ 20: error: value privateField1 cannot be accessed in scopeA.PrivateClass1 val privateField1 = privateClass1.privateField1 ^ 21: error: value privateField2 cannot be accessed in scopeA.PrivateClass1 val privateField2 = privateClass1.privateField2 ^ 22: error: value nested cannot be accessed in scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ 27: error: private class PrivateClass4 escapes its defining scope as part of type scopeA.PrivateClass4 class PrivateClass5 extends PrivateClass4 ^ 28: error: private class PrivateClass4 escapes its defining scope as part of type scopeA.PrivateClass4 protected class PrivateClass6 extends PrivateClass4 ^ 33: error: class PrivateClass4 cannot be accessed in package scopeA class PrivateClass4B extends scopeA.PrivateClass4 ^ 9 errors found 現在,PrivateClass2 不能訪問它的父類PrivateClass1 的private 成員.正如錯誤消息指出的,它們對於子類來說完全不可見.它們也不能存取嵌套類的private 欄位.


正如protected 訪問的例子一樣,PrivateClass3 不能訪問它使用的PrivateClass1 實例的private 成員.不過注意,equalFields 方法可以訪問其它實例的private 成員.
PrivateClass5 和PrivateClass6 的聲明失敗了,如果允許的話,它們等於允許PrivateClass4 「跳出它的定義域」.然而,PrivateClass7 的聲明成功了,它同時被定義為了private.令人好奇的是,我們上一個例子中能夠正確地定義一個繼承自protected 類的public 類.
,和protected 類型聲明一樣,private 類型不能在包含它的package 之外被繼承.
局部 Private 和Protected 可見性
Scala 允許你用scoped private 和protected 可見性聲明來更精細地調整可見性的範圍.注意,在局部聲明中使用proviate 或者protected 是可互換的,它們除了應用到繼承的成員上的可見性之外,其它都是一樣的.
提示
雖然在大多數情況下選擇任何一個都能活動相同的效果,在代碼中使用private 還是比protected 更常見一些.在Scala 的核心庫里,這個比利大概是5:1.
讓我們從scoped private 和scoped protected 之間的唯一區別入手,來看看當成員有這些局部性聲明的時候,它們在繼承機制下是如何工作的.
// code-examples/BasicOOP/scoping/scope-inheritance-wont-compile.scala // WON'T COMPILE package scopeA { class Class1 { private[scopeA] val scopeA_privateField = 1 protected[scopeA] val scopeA_protectedField = 2 private[Class1] val class1_privateField = 3 protected[Class1] val class1_protectedField = 4 private[this] val this_privateField = 5 protected[this] val this_protectedField = 6 } class Class2 extends Class1 { val field1 = scopeA_privateField val field2 = scopeA_protectedField val field3 = class1_privateField // ERROR val field4 = class1_protectedField val field5 = this_privateField // ERROR val field6 = this_protectedField } } package scopeB { class Class2B extends scopeA.Class1 { val field1 = scopeA_privateField // ERROR val field2 = scopeA_protectedField val field3 = class1_privateField // ERROR val field4 = class1_protectedField val field5 = this_privateField // ERROR val field6 = this_protectedField } } 編譯這個文件會產生如下輸出.
17: error: not found: value class1_privateField val field3 = class1_privateField // ERROR ^ 19: error: not found: value this_privateField val field5 = this_privateField // ERROR ^ 26: error: not found: value scopeA_privateField val field1 = scopeA_privateField // ERROR ^ 28: error: not found: value class1_privateField val field3 = class1_privateField // ERROR ^ 30: error: not found: value this_privateField val field5 = this_privateField // ERROR ^ 5 errors found Class2 里的前兩個錯誤說明,在同一個package 內的繼承類,不能引用父類或者this 的scoped private 成員,但是它可以引用包含Class1 和Class2 的package (或者類型)的private 成員.


相比之下,對於package 之外的繼承類,它無法訪問Class1 的任何一個scoped private 成員.
然而,所有的scoped protected 成員對於兩個繼承類來說都是可以見的.
我們會在後面剩下的例子和討論中使用scoped private 聲明,在Scala 庫中,scoped private 比scoped protected 更加常見一些,前面的繼承情況並不是其中一個因素.
,讓我們從最嚴格的可見性開始,private[this],它也對類型成員起作用.
// code-examples/BasicOOP/scoping/private-this-wont-compile.scala // WON'T COMPILE package scopeA { class PrivateClass1(private[this] val privateField1: Int) { private[this] val privateField2 = 1 def equalFields(other: PrivateClass1) = (privateField1 == other.privateField1) && // ERROR (privateField2 == other.privateField2) && (nested == other.nested) class Nested { private[this] val nestedField = 1 } private[this] val nested = new Nested } class PrivateClass2 extends PrivateClass1(1) { val field1 = privateField1 // ERROR val field2 = privateField2 // ERROR val nField = new Nested().nestedField // ERROR } class PrivateClass3 { val privateClass1 = new PrivateClass1(1) val privateField1 = privateClass1.privateField1 // ERROR val privateField2 = privateClass1.privateField2 // ERROR val privateNField = privateClass1.nested.nestedField // ERROR } } 編譯這個文件會產生如下輸出.
5: error: value privateField1 is not a member of scopeA.PrivateClass1 (privateField1 == other.privateField1) && ^ 14: error: not found: value privateField1 val field1 = privateField1 ^ 15: error: not found: value privateField2 val field2 = privateField2 ^ 16: error: value nestedField is not a member of PrivateClass2.this.Nested val nField = new Nested().nestedField ^ 20: error: value privateField1 is not a member of scopeA.PrivateClass1 val privateField1 = privateClass1.privateField1 ^ 21: error: value privateField2 is not a member of scopeA.PrivateClass1 val privateField2 = privateClass1.privateField2 ^ 22: error: value nested is not a member of scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ 7 errors found 注意
第6 到8 行無法解析.它們是第5 行開始的表達式的一部分,編譯器在遇到第一個錯誤之後就會停住.
這些private[this] 成員僅對同一個實例內的成員可見.同一個類的不同實例之間無法訪問對方的private[this] 成員,equalFields 方法無法通過解析.


否則,類成員的可見性就和沒有域限定符的private 一樣了.
當用private[this] 聲明一個類型時,this 的使用被有效的綁定到了包含它的package 里,正如這裡所展示的.
// code-examples/BasicOOP/scoping/private-this-pkg-wont-compile.scala // WON'T COMPILE package scopeA { private[this] class PrivateClass1 package scopeA2 { private[this] class PrivateClass2 } class PrivateClass3 extends PrivateClass1 // ERROR protected class PrivateClass4 extends PrivateClass1 // ERROR private class PrivateClass5 extends PrivateClass1 private[this] class PrivateClass6 extends PrivateClass1 private[this] class PrivateClass7 extends scopeA2.PrivateClass2 // ERROR } package scopeB { class PrivateClass1B extends scopeA.PrivateClass1 // ERROR } 編譯這個文件會產生如下輸出.
8: error: private class PrivateClass1 escapes its defining scope as part of type scopeA.PrivateClass1 class PrivateClass3 extends PrivateClass1 ^ 9: error: private class PrivateClass1 escapes its defining scope as part of type scopeA.PrivateClass1 protected class PrivateClass4 extends PrivateClass1 ^ 13: error: type PrivateClass2 is not a member of package scopeA.scopeA2 private[this] class PrivateClass7 extends scopeA2.PrivateClass2 ^ 17: error: type PrivateClass1 is not a member of package scopeA class PrivateClass1B extends scopeA.PrivateClass1 ^ four errors found 在同一個package 中,嘗試聲明一個public 或者protected 子類會失敗.只有private 和private[this] 子類是允許的.,PrivateClass2 在scopeA2 里,你不能在scopeA2 之外聲明它.簡單地嘗試在無關的scopeB 中聲明一個使用PrivateClass1 的類也失敗了.
因此,當應用到類型時,private[this] 和Java 的package private 可見性一致.
下面,讓我們來檢查類型級別的可見性,private[T],T 是一個類型.
// code-examples/BasicOOP/scoping/private-type-wont-compile.scala // WON'T COMPILE package scopeA { class PrivateClass1(private[PrivateClass1] val privateField1: Int) { private[PrivateClass1] val privateField2 = 1 def equalFields(other: PrivateClass1) = (privateField1 == other.privateField1) && (privateField2 == other.privateField2) && (nested == other.nested) class Nested { private[Nested] val nestedField = 1 } private[PrivateClass1] val nested = new Nested val nestednestedNested = nested.nestedField // ERROR } class PrivateClass2 extends PrivateClass1(1) { val field1 = privateField1 // ERROR val field2 = privateField2 // ERROR val nField = new Nested().nestedField // ERROR } class PrivateClass3 { val privateClass1 = new PrivateClass1(1) val privateField1 = privateClass1.privateField1 // ERROR val privateField2 = privateClass1.privateField2 // ERROR val privateNField = privateClass1.nested.nestedField // ERROR } } 編譯這個文件會產生如下輸出.


12: error: value nestedField cannot be accessed in PrivateClass1.this.Nested val nestednestedNested = nested.nestedField ^ 15: error: not found: value privateField1 val field1 = privateField1 ^ 16: error: not found: value privateField2 val field2 = privateField2 ^ 17: error: value nestedField cannot be accessed in PrivateClass2.this.Nested val nField = new Nested().nestedField ^ 21: error: value privateField1 cannot be accessed in scopeA.PrivateClass1 val privateField1 = privateClass1.privateField1 ^ 22: error: value privateField2 cannot be accessed in scopeA.PrivateClass1 val privateField2 = privateClass1.privateField2 ^ 23: error: value nested cannot be accessed in scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ 7 errors found 一個private[PrivateClass1] 的成員對於其它實例來說也可見,equalFields 方法可以通過編譯.因此,private[T] 不如private[this] 來得嚴格.注意,PrivateClass1 不能訪問Nested.nestedField,那個欄位被聲明為private[Nested].
提示
當T 的成員被聲明為private[T] 的時候,其行為等同於private.但是它不同於private[this],後者更加嚴格.
如果我們修改Nested.nestedField 的範圍,變成private[PrivateClass1] 會發生什麼呢?讓我們來看看private[T] 如何影響嵌套的類型.
// code-examples/BasicOOP/scoping/private-type-nested-wont-compile.scala // WON'T COMPILE package scopeA { class PrivateClass1 { class Nested { private[PrivateClass1] val nestedField = 1 } private[PrivateClass1] val nested = new Nested val nestednestedNested = nested.nestedField } class PrivateClass2 extends PrivateClass1 { val nField = new Nested().nestedField // ERROR } class PrivateClass3 { val privateClass1 = new PrivateClass1 val privateNField = privateClass1.nested.nestedField // ERROR } } 編譯這個文件會獲得如下輸出.
10: error: value nestedField cannot be accessed in PrivateClass2.this.Nested def nField = new Nested().nestedField ^ 14: error: value nested cannot be accessed in scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ two errors found 現在nestedField 對PrivateClass1 來說可見,但是它對於PrivateClass1 之外來說仍然不可見.這就是private 在Java 中的工作.
讓我們來用一個package 名字檢查其作用範圍.
// code-examples/BasicOOP/scoping/private-pkg-type-wont-compile.scala // WON'T COMPILE package scopeA { private[scopeA] class PrivateClass1 package scopeA2 { private [scopeA2] class PrivateClass2 private [scopeA] class PrivateClass3 } class PrivateClass4 extends PrivateClass1 protected class PrivateClass5 extends PrivateClass1 private class PrivateClass6 extends PrivateClass1 private[this] class PrivateClass7 extends PrivateClass1 private[this] class PrivateClass8 extends scopeA2.PrivateClass2 // ERROR private[this] class PrivateClass9 extends scopeA2.PrivateClass3 } package scopeB { class PrivateClass1B extends scopeA.PrivateClass1 // ERROR } 編譯這個文件會產生如下輸出.


14: error: class PrivateClass2 cannot be accessed in package scopeA.scopeA2 private[this] class PrivateClass8 extends scopeA2.PrivateClass2 ^ 19: error: class PrivateClass1 cannot be accessed in package scopeA class PrivateClass1B extends scopeA.PrivateClass1 ^ two errors found 注意PrivateClass2 無法在scopeA2 之外被繼承,但是PrivateClass3 可以在scopeA 中被繼承,它被聲明為private[ScopeA].
,讓我們來看一下package 級別的類型成員作用域效果.
// code-examples/BasicOOP/scoping/private-pkg-wont-compile.scala // WON'T COMPILE package scopeA { class PrivateClass1 { private[scopeA] val privateField = 1 class Nested { private[scopeA] val nestedField = 1 } private[scopeA] val nested = new Nested } class PrivateClass2 extends PrivateClass1 { val field = privateField val nField = new Nested().nestedField } class PrivateClass3 { val privateClass1 = new PrivateClass1 val privateField = privateClass1.privateField val privateNField = privateClass1.nested.nestedField } package scopeA2 { class PrivateClass4 { private[scopeA2] val field1 = 1 private[scopeA] val field2 = 2 } } class PrivateClass5 { val privateClass4 = new scopeA2.PrivateClass4 val field1 = privateClass4.field1 // ERROR val field2 = privateClass4.field2 } } package scopeB { class PrivateClass1B extends scopeA.PrivateClass1 { val field1 = privateField // ERROR val privateClass1 = new scopeA.PrivateClass1 val field2 = privateClass1.privateField // ERROR } } 編譯這個文件會獲得如下輸出.
28: error: value field1 cannot be accessed in scopeA.scopeA2.PrivateClass4 val field1 = privateClass4.field1 ^ 35: error: not found: value privateField val field1 = privateField ^ 37: error: value privateField cannot be accessed in scopeA.PrivateClass1 val field2 = privateClass1.privateField ^ three errors found 唯一的錯誤是嘗試從無關的package scopeB 訪問scopeA 的成員,或者嘗試訪問屬於嵌套的package scopeA2 的成員.
提示
當一個類型或成員被聲明為private[P],P是一個包含它們的package, 那麼這就等同於Java 的package private 可見性.
對於可見性的總結
Scala 可見性聲明非常靈活,並且行為準則一致.它們為所有可能的作用域提供了細緻的可見性控制,從實例級別(private[this])到package 級別(private[P]).例如,它們是的創建在頂層package 之外暴露類型的組件更加容易,很好得在組件package 內部隱藏了類型和類型成員的實現.
,我們觀察到了一個潛在trait 隱藏成員的「問題」.


[火星人 ] Scala編程指南:面向對象編程(2)已經有503次圍觀

http://coctec.com/docs/java/show-post-60100.html