我不明白。为什么Scala支持覆盖方法为字段:
abstract class A {
def i: Int;
}
class B extends A {
val i = 123;
}
val obj:A = new B();
println(obj.i)
方法i
与字段i
相同。为什么呢?
答案 0 :(得分:9)
一种快速查看此处发生的事情的方法是编译此代码并使用javap
检查生成的类。在这里,我们得到A
的以下内容:
public abstract class A implements scala.ScalaObject {
public abstract int i();
public A();
}
对于B
(-p
,我们看到任何私人成员):
public class B extends A implements scala.ScalaObject {
private final int i;
public int i();
public B();
}
所以val
最终只是一个带有getter的私有字段 - 就像你在idiomatic Java中编写的一样,尽管命名约定不同(并且实际上不是约定< / em>,因为编译器会为你处理它。)
答案 1 :(得分:4)
Travis的回答解释如何,在实现级别,某些方法可以被Scala中的字段覆盖,但不能为什么。 why 问题的答案与两种编程语言设计原则有关:Uniform Access Principle和Liskov Substitution Principle。
统一访问原则实质上表示调用者不必区分通过方法读取属性和通过字段读取属性 - 两个调用应该看起来相同,不应该背叛它们涉及存储或计算。
Liskov替换原则基本上表示子类型必须遵守其父类型的所有合同。子类型 允许兑现比其父类型更强的合同,但不得违反其父母的任何合同。
在Scala中,声明没有参数列表的def
意味着某个合同。也就是说,它意味着访问相关属性将返回声明类型的值。由于参考透明度不是保证,后续访问可能会也可能不会返回相同的值。
显然,val
符合相同的合同。但是,val
会提供更强大的保证:每次访问都会产生相同的值。由于这是一个更强大的合同,它与Liskov替换原则完全一致,因为子类型使用val
来实现def
。同样,禁止子类型使用def
来实现val
。