１２章４４
Published: 2024-04-24 01:00:37


```java
// 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()を呼び出すことはできない。
    }
    // ...
}
```

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

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

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

したがって、SubSample(int price) のコンストラクタでは、適切な super(...); の呼び出しが必要です。例えば、親クラスのフィールド name と num にデフォルト値を設定することでこれを実現できます。
2. `SubSample(String name, int num, int price)` コンストラクタでは、`super(name, num);` の後に `this(price);` を呼び出していますが、これはコンパイルエラーを引き起こします。`this` または `super` 呼び出しは、コンストラクタの最初の命令でなければならないためです。さらに、一つのコンストラクタで `super` と `this` の両方を呼ぶことはできません。

## 追記

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

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

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

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

```java
// 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フィールドを直接設定する
    }
    // ...
}
```

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

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

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

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

```java
// 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);
    }
}
```

### 修正点の説明

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

2. **`SubSample(String name, int num, int price)`コンストラクタ**
   - `super(name, num);`で親クラスのコンストラクタを呼び出し、`name`と`num`を初期化します。
   - `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
```