【Java】【C#】継承関係にあるクラスをインスタンス化した時の、参照変数の型による違い

<h1>はじめに</h1>
継承ってどんな動きをするのか、勉強した時のメモです。
<br><br>
[2023/04/14追記]<br>
shiracamus様より頂いたコメントをもとに、C#でも動きを確認してみました！
<h1>動作環境</h1>
Windows10 22H2 OSビルド 19045.2728<br>
Eclipse 2021-12 M2 (4.22.0 M2)<br>
Visual Studio Community 2019<br>
C# 7.3<br>

<h1>やったこと</h1>
<p>Parentクラス、それを継承するChildクラス、エントリーポイントを持つMainクラスを用意。
Mainクラスは、以下のそれぞれの動きを確認しました。</p>

①親クラス型で親クラスのインスタンスを生成
②親クラス型で子クラスのインスタンスを生成

以上で、アクセスできるメンバや、出力結果にどのような違いがあるのか整理しました。

<h1>■Javaの場合</h1>
<h2>Parentクラス</h2>

```Java:Parent.java
package package1;

public class Parent {
	int num = 10;
	String str = "aaa";
	void method1() {
		System.out.println("parent_method");
	}
}
```

<h2>Childクラス</h2>

```Java:Child.java
package package1;

public class Child extends Parent{
	int num = 20;
	int amount = 999;
	void method1() {
		System.out.println("child_method");
	}
	void method2() {
		System.out.println("childchild_method");
	}
}
```

<h2>Mainクラス</h2>
<h3>①親クラス型で親クラスのインスタンスを生成</h3>

```Java:Main.java
package package1;

public class Main {

	public static void main(String[] args) {
		Parent parent = new Parent();
		System.out.println(parent.num);
		System.out.println(parent.str);
		parent.method1();
	}

}
```

<h4>実行結果</h4>

```
10
aaa
parent_method
```

そりゃそう。

<h3>②親クラス型で子クラスのインスタンスを生成</h3>

```Java:Main.java
package package1;

public class Main {

	public static void main(String[] args) {
		Parent parentChild = new Child();
		System.out.println(parentChild.num);
		System.out.println(parentChild.str);
		System.out.println(parentChild.amount);  //←コンパイルエラー
		parentChild.method1();
		parentChild.method2();  //←コンパイルエラー
	}

}
```

コンパイルエラーのところは、一旦コメントアウトして実行
<h4>実行結果</h4>

```
10
aaa
child_method
```

<h4>分かったこと</h4>

+ amountとmethod2()のところでコンパイルエラー
⇒あくまで参照変数の型（Parentクラス）に定義されたメンバにしかアクセスできない

+ numの値が10
⇒同じ名前のフィールドが定義されている場合、参照変数の型の方を見にいく

+ strの値がaaa
⇒きっちり継承されている

+ method1()の実行結果がchild_method
⇒メソッドはフィールドと違って、オーバーライドされたメソッドを見にいく

<h1>■C#の場合</h1>
<h2>Parentクラス</h2>

```Program.cs
public class Parent
{
    public int num = 10;
    public string str = "aaa";
    public void method1()
    {
        Console.WriteLine("parent_method");
    }
}
```

<h2>Childクラス</h2>

```Program.cs
public class Child : Parent
{
    public int num = 20;
    public int amount = 999;
    public void method1()
    {
        Console.WriteLine("child_method");
    }
    public void method2()
    {
        Console.Write("childchild_method");
    }
}
```

<h2>Mainクラス</h2>
<h3>①親クラス型で親クラスのインスタンスを生成</h3>
省略。想定通り、10、aaa、parent_methodが出力される。

<h3>②親クラス型で子クラスのインスタンスを生成</h3>

```Program.cs
public class MainClass
{
    static void Main(string[] args)
    {
        Parent parentChild = new Child();
        Console.WriteLine(parentChild.num);
        Console.WriteLine(parentChild.str);
        Console.WriteLine(parentChild.amount);  //←コンパイルエラー
        parentChild.method1();
        parentChild.method2();   //←コンパイルエラー
    }
}
```

コンパイルエラーのところは、一旦コメントアウトして実行
<h4>実行結果</h4>

```
10
aaa
parent_method
```

<h4>分かったこと</h4>

+ amountとmethod2()のところでコンパイルエラー
⇒あくまで参照変数の型（Parentクラス）に定義されたメンバにしかアクセスできない（**Javaと同様**）

+ numの値が10
⇒同じ名前のフィールドが定義されている場合、参照変数の型の方を見にいく（**Javaと同様**）

+ strの値がaaa
⇒きっちり継承されている（**Javaと同様**）

+ method1()の実行結果がchild_method
⇒メソッドもフィールドと同じように、参照変数の型の方を見にいく（**Javaと違う！！！**）

<h2>virtual/overrideキーワードを付けた時の動きを確認</h2>

<h2>Parentクラス</h2>

```Program.cs
public class Parent
{
    public int num = 10;
    public string str = "aaa";
    public virtual void method1()  //←親クラスのメソッドにvirtualキーワード追加
    {
        Console.WriteLine("parent_method");
    }
}
```

<h2>Childクラス</h2>

```Program.cs
public class Child : Parent
{
    public int num = 20;
    public int amount = 999;
    public override void method1()  //←子クラスのメソッドにoverrideキーワード追加
    {
        Console.WriteLine("child_method");
    }
    public void method2()
    {
        Console.Write("childchild_method");
    }
}
```

<h4>実行結果</h4>

```
10
aaa
child_method
```

<h4>分かったこと</h4>

+ method1()の実行結果がchild_method
⇒virtual/overrideキーワードを付けることで、オーバーライドされたメソッドの方を見にいく（Javaと同じ動きをしてくれる）