重载方法和字段
统一访问原则只是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。重要的是它们不会与范围内的任何其它名称冲突。
【相关阅读】