12章77

TANIAOKA, Akihiro - May 20 - - Dev Community

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

// 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

コンパイルエラーの詳細

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

追記

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

修正方法1: 型を一致させる

// 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

修正方法2: ダウンキャストを使用する

// 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

比較と推奨

修正方法1のメリット

  • コードの読みやすさ: 型のキャストを行わないため、コードがシンプルで理解しやすい。
  • 安全性: ダウンキャストによるClassCastExceptionのリスクがない。

修正方法1のデメリット

  • 柔軟性の欠如: subSample型にすると、他のSubSample特有のメソッドを呼び出すことができなくなる。

修正方法2のメリット

  • 柔軟性: subSubSample型として保持するため、SubSample特有のメソッドも使用できる。

修正方法2のデメリット

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

結論

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

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .