参照型の型変換
参照型の型変換ルールは以下の2つ。
①暗黙型変換
サブクラスのオブジェクトをスーパークラスの型で宣言した変数で扱える。また実装クラスのオブジェクトをインタフェースの型で宣言した変数で扱える。
②キャストによる型変換
スーパークラスで宣言した変数で参照しているサブクラスのオブジェクトを、元の型であるサブクラス型で宣言した変数で扱うにはキャストを用いる。インタフェースも同様でインタフェースで宣言した変数で参照している実装クラスのオブジェクトを、元の型である実装クラス型で宣言した変数で扱うにはキャストを使う。
スーパークラスからサブクラスへの変換をダウンキャスト、サブクラスからスーパークラスへの変換をアップキャストという。ダウンキャストでキャストを使った型変換を行うイメージ。
A.javaとB.java(Aを継承。speakをオーバーライド)で実施してみる。
public class A{
public static void speak() {
System.out.println("Hello");
}
public static void goodbye() {
System.out.println("goodbye");
}
}
public class B extends A{
public static void speak() {
System.out.println("Good Morning");
}
public static void thankyou() {
System.out.println("thankyou");
}
}
B型(Bのインスタンス)→A型
public class Main {
public static void main(String[] args) {
B b = new B();
A a = b;
b.speak();
a.speak();
}
}
結果
Good Morning
Hello
staticメソッドの呼び出し
Aクラス、Bクラスともにspeakメソッドにstaticが付与されている。この時ポリモフィズムは適用されず、変数bはBクラスのspeakメソッド、変数aはAクラスのspeakメソッドを呼び出す
今回はアップキャストなので暗黙の型変換をしている。
今度は以下を見てみよう。
public class Main {
public static void main(String[] args) {
B b = new B();
A a = b;
b.speak();
b.thankyou();
b.goodbye();
a.speak();
//a.thankyou();//エラー
a.goodbye();
}
}
結果
Good Morning
thankyou
goodbye
Hello
goodbye
今度はメソッドを増やして確認してみた。b.goodbye()
はAクラスからBクラスへ継承されたメソッドなので、Bクラスでも呼び出すことができる。ではa.thankyou()
はどうだろうか。
public class Main {
public static void main(String[] args) {
B b = new B();
A a = b;
b.speak();
b.thankyou();
b.goodbye();
a.speak();
a.thankyou();//エラー
a.goodbye();
}
}
結果
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
メソッド thankyou() は型 A で未定義です
at Main.main(Main.java:9)
thankyouというメソッドはBクラスにあってAクラスに無い。今回A型の変数aを使って呼び出そうとしているためエラーになった。
A型(Aのインスタンス)→B型
public class Main {
public static void main(String[] args) {
A a = new A();
B b = a;
}
}
結果
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
型の不一致: A から B には変換できません
at Main.main(Main.java:4)
このエラーの原因はキャストを使わなかったからだ。キャストを追加して再度実行してみる。
public class Main {
public static void main(String[] args) {
A a = new A();
B b = (B)a;
}
}
結果
Exception in thread "main" java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
at Main.main(Main.java:4)
今度はコンパイルが通ったものの、例外が発生した。今回A型の変数aを定義し、参照先のインスタンスもAで指定している。その参照をB型の変数bに代入しようとすると、参照先のインスタンスにはBの差分が含まれていないためClassCastExcepionが発生する。
ではインスタンスを変えて実行しよう。
A型(Bのインスタンス)→B型
public class Main {
public static void main(String[] args) {
A a = new B();
B b = (B)a;
a.speak();
b.speak();
}
}
結果
Hello
Good Morning
今度は成功している。これはA型の変数aが参照しているインスタンスがB型だったために、キャストによる変換がうまくいったということだ。挙動としてはB型(Bのインスタンス)→A型のパターンと同じ。
まとめ
- 参照型の型変換には暗黙型変換とキャストによる型変換がある
- Aと、Aを継承したBで確認。B型(Bのインスタンス)→A型の変換は暗黙型変換。
ただし、BにあってAに無いメソッドは使えない。 - A型(Aのインスタンス)→B型の変換は、Bの差分を持っていないためキャストしてもできない
- A型(Bのインスタンス)→B型はキャストによる型変換で可能