１２章77
Published: 2024-05-20 23:30:23

以下にコードにコメントを追加して、なぜコンパイルエラーが発生するのか説明します。

```java
// Sampleクラス
public class Sample {
    public void test() {
        System.out.println("Sample#test()");
    }
}

// SubSampleクラス。Sampleクラスを継承している。
public class SubSample extends Sample {
    @Override
    public void test() {
        System.out.println("SubSample#test()");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        // SubSample型の変数subを作成し、SubSampleのインスタンスを割り当てる
        SubSample sub = new SubSample();
        
        // Sample型の変数sampleを作成し、SubSampleのインスタンスを割り当てる
        Sample sample = new SubSample();
        
        // subにsampleを代入する。sampleはSample型であり、subはSubSample型である。
        // ここで型の不一致が発生するため、コンパイルエラーが発生する。
        sub = sample;

        // 代入後のsubは実際にはSample型のインスタンスを参照している。
        // しかし、Javaの動的バインディングによりSubSample#test()が呼び出される。
        sub.test();
        
        // sampleは元々SubSampleのインスタンスを参照しているので、SubSample#test()が呼び出される。
        sample.test();
    }
}
```

### コンパイルエラーの詳細
`sub = sample;` の行でコンパイルエラーが発生するのは、`sub`が`SubSample`型であり、`sample`が`Sample`型であるためです。Javaでは、サブクラス型の変数にスーパークラス型のオブジェクトを直接代入することはできません。このため、型の不一致が発生し、コンパイルエラーとなります。


## 追記

2つの修正方法のうち、どちらがベターかは状況によりますが、以下の点を考慮して判断することができます。

### 修正方法1: 型を一致させる
```java
// Sampleクラス
public class Sample {
    public void test() {
        System.out.println("Sample#test()");
    }
}

// SubSampleクラス。Sampleクラスを継承している。
public class SubSample extends Sample {
    @Override
    public void test() {
        System.out.println("SubSample#test()");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        // Sample型の変数subを作成し、SubSampleのインスタンスを割り当てる
        Sample sub = new SubSample();
        
        // Sample型の変数sampleを作成し、SubSampleのインスタンスを割り当てる
        Sample sample = new SubSample();
        
        // subにsampleを代入する。両方ともSample型なので問題ない。
        sub = sample;

        // subはSubSampleのインスタンスを参照しているため、SubSample#test()が呼び出される。
        sub.test();
        
        // sampleもSubSampleのインスタンスを参照しているため、SubSample#test()が呼び出される。
        sample.test();
    }
}
```

### 修正方法2: ダウンキャストを使用する
```java
// Sampleクラス
public class Sample {
    public void test() {
        System.out.println("Sample#test()");
    }
}

// SubSampleクラス。Sampleクラスを継承している。
public class SubSample extends Sample {
    @Override
    public void test() {
        System.out.println("SubSample#test()");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        // SubSample型の変数subを作成し、SubSampleのインスタンスを割り当てる
        SubSample sub = new SubSample();
        
        // Sample型の変数sampleを作成し、SubSampleのインスタンスを割り当てる
        Sample sample = new SubSample();
        
        // subにsampleをキャストして代入する。これにより型の不一致が解消される。
        sub = (SubSample) sample;

        // subはSubSampleのインスタンスを参照しているため、SubSample#test()が呼び出される。
        sub.test();
        
        // sampleもSubSampleのインスタンスを参照しているため、SubSample#test()が呼び出される。
        sample.test();
    }
}
```

### 比較と推奨

#### 修正方法1のメリット
- **コードの読みやすさ**: 型のキャストを行わないため、コードがシンプルで理解しやすい。
- **安全性**: ダウンキャストによる`ClassCastException`のリスクがない。

#### 修正方法1のデメリット
- **柔軟性の欠如**: `sub`を`Sample`型にすると、他の`SubSample`特有のメソッドを呼び出すことができなくなる。

#### 修正方法2のメリット
- **柔軟性**: `sub`を`SubSample`型として保持するため、`SubSample`特有のメソッドも使用できる。

#### 修正方法2のデメリット
- **安全性のリスク**: ダウンキャストは実行時に`ClassCastException`を引き起こす可能性があるため、キャストする前に型を確認する必要がある。
- **コードの複雑化**: キャスト操作はコードの読みやすさを低下させる可能性がある。

### 結論
**修正方法1**を推奨します。特に、キャストが不要であり、コードがよりシンプルかつ安全であるためです。ただし、`sub`を`SubSample`型として使用する必要がある場合や、`SubSample`特有のメソッドを呼び出す必要がある場合は、**修正方法2**を検討してください。

最終的な選択は、アプリケーションの設計や要件に依存します。もしサブクラスの特定の機能を利用する必要があるなら、キャストを使用する方法が適切です。さもなければ、コードのシンプルさと安全性を優先して、型を一致させる方法が望ましいです。