理解できない。Scalaがフィールドとしてオーバーライドメソッドをサポートするのはなぜですか?
abstract class A {
def i: Int;
}
class B extends A {
val i = 123;
}
val obj:A = new B();
println(obj.i)
メソッドi
はフィールドとしてi
です。なんで?
理解できない。Scalaがフィールドとしてオーバーライドメソッドをサポートするのはなぜですか?
abstract class A {
def i: Int;
}
class B extends A {
val i = 123;
}
val obj:A = new B();
println(obj.i)
メソッドi
はフィールドとしてi
です。なんで?
ここで何が起こっているかを確認する簡単な方法の1つは、このコードをコンパイル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
ではありません)、慣用的なJavaで記述したものとよく似たゲッター付きのプライベートフィールドになります。
Travisの回答は、実装レベルで、一部のメソッドをScalaのフィールドでオーバーライドする方法を説明していますが、その理由は説明していません。なぜ質問への答えは、2つのプログラミング言語設計原則に関連しています:統一アクセス原則とリスコフ置換原則。
統一アクセスの原則は、基本的に、呼び出し元がメソッドを介してプロパティを読み取ることとフィールドを介してプロパティを読み取ることを区別する必要がないことを示しています。どちらの呼び出しも同じように見え、ストレージと計算のどちらを含むかを裏切らないようにする必要があります。
リスコフの置換原則は、基本的に、サブタイプはその親タイプのすべての契約を尊重しなければならないと述べています。サブタイプは、その親タイプよりも強力なコントラクトを尊重できますが、その親のコントラクトに違反してはなりません。
Scalaではdef
、パラメーターリストなしでaを宣言することは、特定のコントラクトを意味します。つまり、関連付けられたプロパティにアクセスすると、宣言された型の値が返されることを意味します。参照透過性は保証されていないため、以降のアクセスでは同じ値が返される場合と返されない場合があります。
明らかに、aval
は同じ契約を満たしています。ただし、aval
はさらに強力な保証を行います。すべてのアクセスが同じ値になります。これはより強力なval
コントラクトであるため、サブタイプがを実装するために使用するリスコフの置換原則と完全に一致していdef
ます。同じように、サブタイプがを使用してdef
を実装することは禁止されていval
ます。