为了方便,我们会使用通用的“类型” 这一词语来指代类和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 包来说不可见。
编辑特别推荐:
Java读取文件内容再编辑
JS获取单选与多选按纽的值
每一种文件类型所对应的ContentType