JavaのようなOOプログラミング言語での継承について質問があります。メソッドとその呼び出しをコンパイルする方法を説明したとき、それは私のコンパイラクラスで思い付きました。コンパイルするソース言語の例としてJavaを使用していました。
次に、このJavaプログラムについて考えてみましょう。
class A {
public int x = 0;
void f () { System.out.println ( "A:f" ); } }
class B extends A {
public int x = 1;
void f () { System.out.println ( "B:f" ); } }
public class Main {
public static void main ( String [] args ) {
A a = new A ();
B b = new B ();
A ab = new B ();
a.f();
b.f();
ab.f();
System.out.println ( a.x );
System.out.println ( b.x );
System.out.println ( ab.x ); } }
実行すると、次の結果が得られます。
A:f
B:f
B:f
0
1
0
興味深いのは、動的ab
なstatic型のオブジェクトで発生するケースです。プリントアウトとしてA
B
ab.f()
B:f
つまり、メソッドの呼び出しは、メソッドの呼び出しに使用されたオブジェクトのコンパイル時のタイプの影響を受けません。ただし、が
System.out.println ( ab.x )
出力される0
ため、メンバーアクセスはコンパイル時の型の影響を受けます。
学生はこの違いについて質問しました:メンバーのアクセス方法とメソッドは互いに一致してはいけませんか?「それがJavaのセマンティクスだ」よりも良い答えを思いつくことができませんでした。
メンバーとメソッドがこの意味で異なるのは、はっきりとした概念的な理由を知っていますか?生徒に何か教えてもらえますか?
編集:さらなる調査の結果、これはJavaの特異性のようです:C ++とC#の動作は異なります。たとえば、以下のSaeed Amiriのコメントを参照してください。
編集2:対応するScalaプログラムを試してみました。
class A {
val x = 0;
def f () : Unit = { System.out.println ( "A:f" ); } }
class B extends A {
override val x = 1;
override def f () : Unit = { System.out.println ( "B:f" ); } }
object Main {
def main ( args : Array [ String ] ) = {
var a : A = new A ();
var b : B = new B ();
var ab : A = new B ();
a.f();
b.f();
ab.f();
System.out.println ( "a.x = " + a.x );
System.out.println ( "b.x = " + b.x );
System.out.println ( "ab.x = " + ab.x ); } }
そして驚いたことに、これは
A:f
B:f
B:f
a.x = 0
b.x = 1
ab.x = 1
overrise
修飾子が必要であることに注意してください。ScalaはJVMにコンパイルされ、さらに、Scalaコンパイラ/ランタイムを使用してJavaプログラムを最上部でコンパイルおよび実行すると、Javaプログラムのように動作するので、これは私を驚かせます。
f
れx
、明示的にこれを言及せずに、IMO C#バージョンの方が優れています(これを行う代わりに、OOの観点でより受け入れやすい)を仮想として定義するとx
、f
それらをでオーバーライドすることにより、整合性の結果が得られますB
。しかし、Stackoverflowでこれを尋ねると、より多くの注目が集まり、きっと素晴らしい答えになるでしょう。