scala中的无参方法与统一访问原则

无参方法的惯例是:

  1. 方法没有参数。
  2. 方法不会改变可变状态(无副作用)

这个惯例支持统一访问原则(uniform access principle): 客户代码不应由属性是通过字段实现还是方法实现而受影响。

父类中定义的无参函数(def) ,子类可以用一个字段来实现(val/var)
比如:

abstract class A { def a : Int;}

子类可以为:

class B extends A { val a = 1 }  //里面的val也可以写成var

当然val成员也可以在子类中被override

class A { val a = 2 } 
class B extends A { override val a = 3 }

但父类中成员声明为var则子类用val重写是不行的,因为var提供了getter/setter,而val只有getter:

abstract class A { var a:Int }
class B extends A { val a = 1}

error: class B needs to be abstract, since variable a in class A of type Int is not defined (Note that an abstract var requires a setter in addition to the getter)

如果一个类中,出现了同名的 成员变量和无参函数,则编译时会报错(有参则没有问题),这点与java不同。

java中有4个命名空间:

  1. 类型
  2. 方法
  3. 字段

方法与字段是不同的命名空间,所以字段与方法同名是不会冲突的。

而scala中仅有2个命名空间:

  1. 值(字段/方法/包/单例)
  2. 类型(类/特质)
    所以在scala可以实现用val重写无参方法这种事情。

不过把字段、方法、单例 放在同一个命名空间还好理解,但“包”也与它们在同一个命名空间是怎么回事?
scala里包与字段和方法共享相同命名空间是为了让你引入包,而不仅仅是引入类型名以及单例对象的字段和方法。这点也与java不同,java只能import一个包下的类。

import java.{util => u} 

class A {
  val a = new u.ArrayList[String](); 
  def u = 2 //命名冲突
}

原则上,scala中的函数都可以省略空括号,然而在调用的方法超出其调用者对象的属性时,推荐仍保持空括号。
比如,方法执行了I/O, 或有改变可变状态,或读取了不是调用者字段的var,总之无论是直接还是非直接的使用可变对象
都应该加空括号

"hello".length  // 没有副作用,可以省略括号

println()  // 最好别省略()

上面是从有无副作用的情况下考虑的,另一种考虑方式是如果函数执行了逻辑操作就使用括号,如果仅是提供了对属性的访问则可以省略。

发表评论

电子邮件地址不会被公开。 必填项已用*标注