【Java初学者】参照型のダウンキャストがわからなかった話

Pocket

はじめに

Etse sen(元気ですか)? Adjoaです。私は現在、システムエンジニアとして働いています。

現在、Java Silverの取得を目指して学習中です。その際、参照型の「ダウンキャスト」について理解するのに苦労しました。本記事では、備忘録も兼ねて、私が理解した内容を整理して記録します(内容的には Java Bronze レベルです)。

Java初学者や、Java SE Bronzeの取得を目指す方などのご参考になれば幸いです。

アップキャスト/ダウンキャストとは

あるクラスのインスタンスを、そのクラスが継承しているスーパークラスや実装しているインタフェースの型として扱うことを「アップキャスト」といいます。

逆に、スーパークラスやインタフェース型の参照を、実際のインスタンスが属するサブクラス型に明示的に変換して扱うことを「ダウンキャスト」といいます。

サンプルコード

以下のようなクラス構成を例に説明します。

InterfaceSampleクラス

public interface InterfaceSample{
    // any code
}

ImplSampleクラス(InterfaceSampleクラスを実装)

public class ImplSample implements InterfaceSample {
    // any code
}

ExtSampleクラス(ImplSampleクラスを継承)

class ExtSample extends ImplSample {
    // any code
}

わからなかったこと

InterfaceMainクラス(例外が発生するバージョン

public class InterfaceMain {
    public static void main(String[] args) {
        ImplSample implSample = new ImplSample();
        InterfaceSample interfaceSample = implSample; // アップキャストはできる
        ExtSample extSample = (ExtSample) implSample; //ここで例外が発生
    }
}

実行時の例外

Exception in thread "main" java.lang.ClassCastException: class ImplSample cannot be cast to class ExtSample (ImplSample and ExtSample are in unnamed module of loader 'app')
        at InterfaceMain.main(InterfaceMain.java:5)

ダウンキャストで例外が発生する

「明示的にダウンキャストすれば、スーパークラスのインスタンスをサブクラスの変数に代入できるはず」

──そう思い込んでいました。(実はこの「スーパークラスのインスタンス」という部分が完全な誤解で、それに気づくまでにかなり時間がかかりました!)

サブクラスはスーパークラスの定義を継承しているため、スーパークラス型として扱うことができます(アップキャスト。InterfaceMainの4行目)。ということは、その逆も可能だろう──そう考えてしまったのです。

しかし、実際にコードを実行してみると、ClassCastException が発生します。

わかったこと

スーパークラスは、サブクラス固有の情報(=差分)を持っていません。そのため、スーパークラスのインスタンス(参照先の実体)をサブクラスへダウンキャストすることはできなかったのです。

InterfaceMainクラス ※正常に動くバージョン

public class InterfaceMain {
    public static void main(String[] args) {
        InterfaceSample interfaceSample = new ExtSample();
        ExtSample extSample = (ExtSample)interfaceSample;
        if (extSample instanceof ExtSample) {
            System.out.println("Downcasting successful");
        }        
    }
}

このように、スーパークラスの型(InterfaceSample)で宣言されていても、実際のインスタンスがサブクラスの型(ExtSample)であれば、ダウンキャストは可能です。

正常に動くバージョンの実行結果

おわりに

ダウンキャストの際に重要なのは、参照している実体がサブクラスのインスタンスであるかどうか、という点です。

この記事が、同じようにつまずいた方の助けになれば幸いです。最後までお読みいただき、ありがとうございました!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です