12章44

TANIAOKA, Akihiro - Apr 24 - - Dev Community
// SubSampleクラス定義部、Sampleクラスを継承
public class SubSample extends Sample {
    // ...

    // SubSampleクラスのコンストラクタ(価格を引数に取る)
    // このコンストラクタは、親クラスSampleのnameとnumを初期化していないため、
    // SubSampleオブジェクトが作成された際に、nameとnumがデフォルト値のnullと0で初期化される
    public SubSample(int price) {
         //ここにsuper(); を呼び出す記述がないのでコンパイルエラーが発生
        this.price = price; 
    }

    // コンストラクタでコンパイルエラーが発生する箇所(文字列、数値、価格を引数に取る)
    public SubSample(String name, int num, int price) {
        super(name, num); // 親クラスのコンストラクタを呼び出し
        this(price); // コンパイルエラー:他のコンストラクタを呼び出す場合、それは最初の命令でなければならない。
                     // また、super()が既に呼び出されているため、this()を呼び出すことはできない。
    }
    // ...
}
Enter fullscreen mode Exit fullscreen mode

ここで指摘する二つの問題点は以下の通りです:

  1. Javaの継承において、すべてのコンストラクタは、直接または間接的に親クラスのコンストラクタを呼び出さなければなりません。もし親クラスにデフォルトコンストラクタ(引数なしのコンストラクタ)が存在しない場合、サブクラスのコンストラクタは明示的に親クラスの存在するコンストラクタを呼び出す必要があります。

具体的には、SubSample(int price) コンストラクタで super(); を呼び出す記述がないと、コンパイルエラーが発生します。これは、Sample クラスにデフォルトコンストラクタが存在しないためです。Sample クラスには引数を取るコンストラクタのみが定義されている場合、SubSample コンストラクタはこれを明示的に呼び出す必要があります。

したがって、SubSample(int price) のコンストラクタでは、適切な super(...); の呼び出しが必要です。例えば、親クラスのフィールド name と num にデフォルト値を設定することでこれを実現できます。

  1. SubSample(String name, int num, int price) コンストラクタでは、super(name, num); の後に this(price); を呼び出していますが、これはコンパイルエラーを引き起こします。this または super 呼び出しは、コンストラクタの最初の命令でなければならないためです。さらに、一つのコンストラクタで superthis の両方を呼ぶことはできません。

追記

コンパイルエラーを回避するためには、SubSample クラスのコンストラクタを修正する必要があります。次のように変更できます:

  1. SubSample(int price) コンストラクタでは、親クラス Sample のデフォルトコンストラクタを呼び出して適切に初期化するか、もしくは親クラスにデフォルトコンストラクタを追加する必要があります。ただし、現在の Sample クラスには引数なしのデフォルトコンストラクタが定義されていないため、SubSample コンストラクタ内で親クラスのフィールドを明示的に初期化するか、親クラスにデフォルトコンストラクタを追加する必要があります。

  2. SubSample(String name, int num, int price) コンストラクタでは、this(price); の呼び出しを削除し、代わりに price フィールドを直接設定します。

これらの変更を加えたコードは次のようになります:

// Sampleクラスにデフォルトコンストラクタを追加
public class Sample {
    // ...
    // デフォルトコンストラクタを追加(オプション)
    public Sample() {
        // デフォルトの値でフィールドを初期化
        this.name = null;
        this.num = 0;
    }
    // ...
}

public class SubSample extends Sample {
    // ...

    // SubSampleクラスのコンストラクタ(価格を引数に取る)
    public SubSample(int price) {
        super(); // 親クラスSampleのデフォルトコンストラクタを呼び出し(もし追加した場合)
        this.price = price;
    }

    // SubSampleクラスのコンストラクタ(文字列、数値、価格を引数に取る)
    public SubSample(String name, int num, int price) {
        super(name, num); // 親クラスのコンストラクタを呼び出す
        this.price = price; // priceフィールドを直接設定する
    }
    // ...
}
Enter fullscreen mode Exit fullscreen mode

これにより、SubSample の各コンストラクタは適切に Sample クラスのコンストラクタを呼び出し、price フィールドも適切に設定するため、コンパイルエラーは発生しなくなります。

追記

コンパイルエラーを回避するためには、SubSampleクラスのコンストラクタを正しく実装する必要があります。具体的には、以下の2つのポイントを修正します:

  1. 親クラスSampleのコンストラクタを呼び出す。
  2. 同じクラス内の他のコンストラクタを呼び出す際に、親クラスのコンストラクタも正しく呼び出す。

修正後のコードは以下の通りです:

// Sampleクラスの定義
public class Sample {
    String name;
    int num;

    // コンストラクタ
    public Sample(String name, int num) {
        this.name = name;
        this.num = num;
    }
}

// Sampleクラスを継承したSubSampleクラスの定義
public class SubSample extends Sample {
    int price;

    // コンストラクタ1: 引数としてpriceのみを受け取る
    public SubSample(int price) {
        // 親クラスのデフォルトコンストラクタを呼び出し
        super("defaultName", 0); // 適切なデフォルト値を与える
        this.price = price;
    }

    // コンストラクタ2: 引数としてname, num, priceを受け取る
    public SubSample(String name, int num, int price) {
        // 親クラスのコンストラクタを呼び出し、nameとnumを初期化
        super(name, num);
        // 自クラスのpriceを初期化
        this.price = price;
    }
}

// メインクラス
public class Main {
    public static void main(String[] args) {
        // SubSampleクラスのインスタンスを生成
        SubSample s1 = new SubSample(100);
        SubSample s2 = new SubSample("sample", 200, 100);

        // インスタンスのフィールドを出力
        System.out.println(s1.name + ", " + s1.num + ", " + s1.price);
        System.out.println(s2.name + ", " + s2.num + ", " + s2.price);
    }
}
Enter fullscreen mode Exit fullscreen mode

修正点の説明

  1. SubSample(int price)コンストラクタ

    • super("defaultName", 0);を追加して、親クラスSampleのコンストラクタを呼び出します。これにより、親クラスのフィールドnamenumにデフォルト値を設定します。
  2. SubSample(String name, int num, int price)コンストラクタ

    • super(name, num);で親クラスのコンストラクタを呼び出し、namenumを初期化します。
    • this.price = price;priceを初期化します。

これで、コンパイルエラーが発生せず、期待通りの動作をします。

補足

出力

SubSample s1 = new SubSample(100);

nameは"defaultName"、numは0、priceは100になります。
出力:defaultName, 0, 100
SubSample s2 = new SubSample("sample", 200, 100);

nameは"sample"、numは200、priceは100になります。
出力:sample, 200, 100
したがって、プログラムを実行すると以下の出力が得られます:

defaultName, 0, 100
sample, 200, 100
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .