こんにちは。今回の記事は演算子instanceofの使い方についてです。
Javaにはクラスやインターフェースの概念があります。ある変数に格納されたインスタンスのクラス型が変数の型そのものではない可能性があります。
例えば自作した関数の仮引数の型がインターフェースの場合、その関数が呼び出されるときは、実引数はそのインターフェースの実装クラスのインスタンスが格納されます。例えば、Listインターフェースを仮引数にして、呼び出されるときはArrayListクラスのインスタンスが渡されるという場合です。
場合によっては、クラスの型で処理を分岐したい場合があるかもしれません。そのようなときに、instanceofが使えると思います。
instanceofの使い方
instanceofを使う場合の記述の仕方は以下です。
- A instanceof B
上記のA(左辺)とB(右辺)の部分は以下です。
A | 対象のインスタンス |
B | クラスまたはインターフェース |
上記の文の実行結果はtrueまたはfalseです。
trueが返却されるのは、Bに対して、Aが以下の場合です。
- クラスBのインスタンス
- クラスBを継承したクラスのインスタンス
- インターフェースBを実装したクラスのインスタンス
Aの個所には変数名を記載することが多いと思います。なお、変数に格納された実際のインスタンスが判定の対象です。
instanceofの結果がtrueになる場合
以下のコードは上記3つに対するサンプルです。3つともtrueになる場合です。
結果がtrueのサンプル3つ
public class Test001 {
public static void main(String[] args) {
Test001 t = new Test001();
t.sample1();
t.sample2();
t.sample3();
}
private void sample1() {
// (1) AはクラスBのインスタンス
Object obj = Integer.parseInt("1");
boolean isInstance = obj instanceof Integer;
System.out.println("(1) " + isInstance);
}
private void sample2() {
// (2) AはクラスBを継承したクラスのインスタンス
Object obj = Integer.parseInt("1");
boolean isInstance = obj instanceof Number;
System.out.println("(2) " + isInstance);
}
private void sample3() {
// (3) AはインターフェースBを実装したクラスのインスタンス
Object s = "abc";
boolean isInstance = s instanceof CharSequence;
System.out.println("(3) " + isInstance);
}
}
結果
(1) true
(2) true
(3) true
上記のメソッドではObject型変数にあるクラスのインスタンスを格納しています。それを演算子instanceofでチェックしています。
(1)では、変数objにInteger型のインスタンスを格納しています。演算子instanceofの右側がIntegerなので、その判定結果はtrueです。
(2)でも、変数objにInteger型のインスタンスを格納しています。演算子instanceofの右側はNumberクラスです。Integerクラスは以下のように定義されています。
public final class Integer extends Number implements ...
上記のため、IntegerクラスはNumberクラスを継承しています。そのため、演算子instanceofによる判定結果はtrueです。
(3)では、変数objにString型のインスタンスを格納しています。演算子instanceofの右側はCharSequenceインターフェースです。Stringクラスは以下のように定義されています。
public final class String implements ... CharSequence,...
上記のため、StringクラスはCharSequenceインターフェースを実装したクラスです。そのため、演算子instanceofによる判定結果はtrueです。
上記のように、演算子instanceofを使うことで型を判定することが出来ます。
instanceofの結果がfalseになる場合
falseになる場合は、以下のサンプルのように比較するクラスが異なる階層のクラスの場合です。
結果がfalseのサンプル
Object obj = Integer.parseInt("1");
boolean isInstance = obj instanceof Double;
System.out.println("(1) " + isInstance);
結果
(1) false
上記では変数objがDouble型であるか判定しています。クラスの階層ツリーで、IntegerとDoubleは以下の位置です。
java.lang.Object ┗java.lang.Number ┣java.lang.Integer ┣java.lang.Double
上記のように、IntegerとDoubleは親子関係にないので、演算子instanceofによる判定結果はfalseです。
以下は、変数がnullの場合を試した結果です。
nullの場合
Object obj = null;
boolean isInstance = obj instanceof Double;
System.out.println(obj);
System.out.println("(1) " + isInstance);
結果
null
(1) false
上記結果の通り、演算子instanceofの左辺がnullの場合、結果はfalseになりました。
型変換を使った例
List型変数のジェネリクスがObject型である場合を考えます。格納されているクラスはDouble型とString型であるとします。また、String型には数値に変換できる文字列が格納されているとします。
List型の要素の数値の合計を計算することを考えます。Double型とString型それぞれで数値に変換するして合計を求めるように実装するとき、演算子instanceofが便利です。以下のようにサンプルを作ってみました。
型変換のサンプル
import java.util.ArrayList;
import java.util.List;
public class Test004 {
public static void main(String[] args) {
Test004 t = new Test004();
t.sample();
}
private void sample() {
List<Object> list = getData();
double total = 0;
for (Object obj : list) {
double value = 0;
if (obj instanceof Double) {
// (1)
value = (Double)obj;
} else if (obj instanceof String) {
// (2)
String s = (String)obj;
value = Double.parseDouble(s);
}
// (3)
total += value;
}
System.out.println("合計:" + total);
}
private List<Object> getData() {
List<Object> list = new ArrayList<Object>();
list.add(Double.valueOf(0.1));
list.add(Double.valueOf(0.2));
list.add("12.09");
return list;
}
}
結果
合計:12.39
上記プログラムでは拡張for文を使っています。変数objに変数Listの要素が格納されます。今回の場合、繰り返しの対象となるList型オブジェクトはgetData()メソッドで作られたオブジェクトです。そのため、変数objに格納されるのはString型かDouble型です。
if文の条件から、(1)は変数objがDouble型の場合に実行されるブロックです。変数valueにdouble型の値を代入するため、(Double)objと記述しています。型をDouble型に変換することで変数valueに値が格納されます。(自動的にdouble型の値として代入されます。)
(2)のブロックは変数objがString型の場合に実行されるブロックです。変数objに格納されたインスタンスはString型なので、最初にString型に変換しています。Double.parseDouble()メソッドがString型を引数に指定できるので、このメソッドでdouble型の値に変換しています。
(3)は合計を加算する箇所です。(1)と(2)で得られた値を加算しています。
上記が型変換を使ったサンプルです。型変換することでそのクラスで定義されたメソッドを呼び出すことが出来できます。
型変換の個所でif文を使わない場合、Eclipse上でコンパイルエラーは発生しませんでした。しかし、実行時では、型変換ができないとエラーが発生しました。例えば以下のように、Double型をString型に変更できないというエラーが発生します。
java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.String
「(String)obj」の個所で、変数objにDouble型のインタンスが格納されていた場合、上記のエラーが実行時に発生します。
非互換条件オペランド型
演算子instanceofの左辺と右辺について、明らかにfalseになるような状態はコンパイルエラーが表示されます。例えば、以下のコードです。
コンパイルエラーが発生する例
String s = "abc";
System.out.println(s instanceof Integer);
String型のインスタンスはInteger型のインスタンスではありません。Eclipseで上記コードを書いた結果、「s instanceof Integer」の部分に赤い波線が表示されます。マウスポインタを合わせると、「非互換条件オペランド型 String と Integer」というエラーが表示されました。
コンパイルエラーが発生しない例
Object s = "abc";
System.out.println(s instanceof Integer);
上記は変数sの型Objectに変えただけです。この場合、コンパイルエラーは発生しませんでした。
以上、参考になれば幸いです。