2013年5月6日月曜日

[Java]コードから動的にinstanceofしたい

特定の型を継承または実装しているオブジェクトに対して、何等か処理をしたい場合、判定にinstanceofを使います。 しかし、instanceofはJavaの構文であるため、動的に判定することはできません。

何とかする方法ないかなぁと調べてみると、以下のような方法がありました。Class.isInstanceかisAssignableFromを使います。
public static void main(String[] args) {

    Object baseClass = new BaseClass();
    Object subClass = new SubClass();

    Object baseClassWithSubOthreType = new BaseClassWithSubOthreType();
    Object subClassWithSubOthreType = new SubClassWithSubOthreType();

    // instance of
    System.out.println("baseClass is Object=" + (baseClass instanceof Object));
    System.out.println("baseClass is BaseClass=" + (baseClass instanceof BaseClass));
    System.out.println("baseClass is SubClass=" + (baseClass instanceof SubClass));
    System.out.println("baseClass is BaseOthreType=" + (baseClass instanceof BaseOthreType));

    System.out.println("subClass is Object=" + (subClass instanceof Object));
    System.out.println("subClass is BaseClass=" + (subClass instanceof BaseClass));
    System.out.println("subClass is SubClass=" + (subClass instanceof SubClass));
    System.out.println("subClass is BaseOthreType=" + (subClass instanceof BaseOthreType));

    System.out.println("baseClassWithSubOthreType is Object=" + (baseClassWithSubOthreType instanceof Object));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (baseClassWithSubOthreType instanceof BaseClass));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (baseClassWithSubOthreType instanceof SubClass));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + (baseClassWithSubOthreType instanceof BaseOthreType));

    System.out.println("subClassWithSubOthreType is Object=" + (subClassWithSubOthreType instanceof Object));
    System.out.println("subClassWithSubOthreType is BaseClass=" + (subClassWithSubOthreType instanceof BaseClass));
    System.out.println("subClassWithSubOthreType is SubClass=" + (subClassWithSubOthreType instanceof SubClass));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + (subClassWithSubOthreType instanceof BaseOthreType));

    // isInstance
    System.out.println("-----------");
    // 比較対象のクラスが自クラスの型に対してい代入可能か判定する
    System.out.println("baseClass is Object=" + Object.class.isInstance(baseClass));
    System.out.println("baseClass is BaseClass=" + BaseClass.class.isInstance(baseClass));
    System.out.println("baseClass is SubClass=" + SubClass.class.isInstance(baseClass));
    System.out.println("baseClass is BaseOthreType=" + BaseOthreType.class.isInstance(baseClass));

    System.out.println("subClass is Object=" + Object.class.isInstance(subClass));
    System.out.println("subClass is BaseClass=" + BaseClass.class.isInstance(subClass));
    System.out.println("subClass is SubClass=" + SubClass.class.isInstance(subClass));
    System.out.println("subClass is BaseOthreType=" + BaseOthreType.class.isInstance(subClass));

    System.out.println("baseClassWithSubOthreType is Object=" + (Object.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (BaseClass.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (SubClass.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isInstance(baseClassWithSubOthreType));

    System.out.println("subClassWithSubOthreType is Object=" + Object.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is BaseClass=" + BaseClass.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is SubClass=" + SubClass.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isInstance(subClassWithSubOthreType));

    // isAssignableFrom
    System.out.println("-----------");
    // 比較対象のクラスが自クラスのサブクラスか判定する
    System.out.println("baseClass is Object=" + Object.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is BaseClass=" + BaseClass.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is SubClass=" + SubClass.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(baseClass.getClass()));

    System.out.println("subClass is Object=" + Object.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is BaseClass=" + BaseClass.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is SubClass=" + SubClass.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(subClass.getClass()));

    System.out.println("baseClassWithSubOthreType is Object=" + (Object.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (BaseClass.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (SubClass.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(baseClassWithSubOthreType.getClass()));

    System.out.println("subClassWithSubOthreType is Object=" + Object.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is BaseClass=" + BaseClass.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is SubClass=" + SubClass.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(subClassWithSubOthreType.getClass()));


}

// BaseClass
static class BaseClass {

}

// SubClass
static class SubClass extends BaseClass {

}

// BaseClass + SubOthreType
static class BaseClassWithSubOthreType implements SubOthreType {

}

// SubClass + SubOthreType
static class SubClassWithSubOthreType extends SubClass implements SubOthreType {

}

// BaseOthreType
interface BaseOthreType {

}

// SubOthreType
interface SubOthreType extends BaseOthreType {

}

■出力結果(どれも同じ結果が出ます)
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true
-----------
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true
-----------
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true

・・・そもそもタイプ別で分岐しないといけないというのは設計が悪い。というケースが多いのかなぁと思います。 しかし、実装レベルで楽をするために。というのはありかなぁと思います。
たとえば、例外発生時のエラーメッセージの判定を一括して行う処理を実装する場合、これをMap<Exceptionのラッパー,String>で処理できるとずいぶんすっきりすると思います。

キーになる情報のhashcode()を工夫すれば、楽に実装できるような気がします。 instanceofだとif文たくさんになる図が目に浮かびますね。

しかし、この2つは、何が違うんでしょう?リファレンスを見ると、
  • isInstanceは、Java 言語の instanceof 演算子と動的に等価
  • isAssignableFromは、型として等しいか。プリミティブのタイプも判定できる
ということらしいです。
・・・じゃあ、相互互換ってどうなんだろう?ということでやってみました。コードと結果は以下の通り

    System.out.println("Integer isInstance int = " + int.class.isInstance(new Integer(1)));
    System.out.println("int isInstance Integer = " + Integer.class.isInstance(1));

    System.out.println("Integer isAssignableFrom int = " + int.class.isAssignableFrom(Integer.class));
    System.out.println("int isAssignableFrom Integer = " + Integer.class.isAssignableFrom(int.class));

--以下出力結果

Integer isInstance int = false
int isInstance Integer = true
Integer isAssignableFrom int = false
int isAssignableFrom Integer = false

Class.isInstanceでは、双方向でtrueを予想していたのですが、Integerとintは互換があり、逆は互換がない。っていう結果になりました。
Class.isAssignableFromに関しては、両方false。理由がよくわからない。。。

0 件のコメント: