C# の本を読んでいたら、クラスのフィールドの初期化順序が Java と違っていてびっくりしました。
Java のフィールド初期化順序
Java でインスタンスを作成した場合、親クラスのフィールド初期化 → 親クラスのコンストラクタ実行 → 子クラスのフィールド初期化 → 子クラスのコンストラクタ実行 の順番で処理が行われます。
そのため、下記では Parent クラスのコンストラクタを呼び出した時点では Child クラスの c フィールドは初期化されていないため、System.out.println での出力が初期値である null になっています。
public class Program { public static void main(String[] args) { new Child(); } public static class Parent { // 順序① - 親クラスのフィールド初期化 protected String p = "parent"; // 順序② - 親クラスのコンストラクタ実行 public Parent() { // 下記の実行結果は "parent, null" System.out.println(this.toString()); } } public static class Child extends Parent { // 順序③ - 子クラスのフィールド初期化 protected String c = "child"; // 順序④ - 子クラスのコンストラクタ実行 public Child() { // 下記の実行結果は "parent, child" System.out.println(this.toString()); } @Override public String toString() { return String.format("%s, %s", p, c); } } }
C# のフィールド初期化順序
一方の C# では、インスタンスを作成した場合、子クラスのフィールド初期化 → 親クラスのフィールド初期化 → 親クラスのコンストラクタ実行 → 子クラスのコンストラクタ実行 の順番で処理が行われます。
そのため、下記では Parent クラスのコンストラクタを呼び出した時点で Child クラスの c フィールドが初期化されているため、Console.WriteLine の出力が "child" になっています。
using System; namespace ConsoleApplication { class Program { static void Main(string[] args) { new Child(); } } public class Parent { // 順序② - 親クラスのフィールド初期化 protected string p = "parent"; // 順序③ - 親クラスのコンストラクタ実行 public Parent() { // 下記の実行結果は "parent, child" Console.WriteLine(this.ToString()); } } public class Child : Parent { // 順序① - 子クラスのフィールド初期化 protected string c = "child"; // 順序④ - 子クラスのコンストラクタ実行 public Child() { // 下記の実行結果は "parent, child" Console.WriteLine(this.ToString()); } public override string ToString() { return string.Format("{0}, {1}", p, c); } } }
こんな違いがあるとは知りませんでした!