web-dev-qa-db-ja.com

instanceofとClass.isAssignableFrom(...)の違いは何ですか?

次のうちどれが良いですか?

a instanceof B

または

B.class.isAssignableFrom(a.getClass())

私が知っている唯一の違いは、 'a'がnullの場合、最初のものはfalseを返し、2番目のものは例外をスローするということです。それ以外は、彼らは常に同じ結果を与えるのですか?

430
Megamug

instanceofを使うときは、コンパイル時にBのクラスを知る必要があります。 isAssignableFrom()を使うとき、それは動的で、実行時に変わることがあります。

468
Marc Novakowski

instanceofは参照型でのみ使用でき、プリミティブ型では使用できません。 isAssignableFrom()はどんなクラスオブジェクトでも使うことができます。

a instanceof int  // syntax error
3 instanceof Foo  // syntax error
int.class.isAssignableFrom(int.class)  // true

http://Java.Sun.com/javase/6/docs/api/Java/lang/を参照してください。 Class.html#isAssignableFrom(Java.lang.Class)

201
Adam Rosenfield

パフォーマンスの面で話す:

TL; DR

同様のパフォーマンスを持つ isInstance または instanceof を使用します。 isAssignableFrom は少し遅くなります。

パフォーマンス順

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isAssignableFrom (+ 2.7%)

Java 8 Windows x64上で2000回の反復のベンチマークに基づき、20回のウォームアップ反復。

理論的には

バイトコードビューア のようなソフトを使用して、各演算子をバイトコードに変換できます。

のコンテキストでは:

package foo;

public class Benchmark
{
  public static final Object a = new A();
  public static final Object b = new B();

  ...

}

Java:

b instanceof A;

バイトコード:

getstatic foo/Benchmark.b:Java.lang.Object
instanceof foo/A

Java:

A.class.isInstance(b);

バイトコード:

ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:Java.lang.Object
invokevirtual Java/lang/Class isInstance((Ljava/lang/Object;)Z);

Java:

A.class.isAssignableFrom(b.getClass());

バイトコード:

ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:Java.lang.Object
invokevirtual Java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual Java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);

各演算子で使用されるバイトコード命令の数を測定すると、 instanceof および isInstance の方が isAssignableFromより速いことが予想されます。 ただし、実際のパフォーマンスはバイトコードではなくマシンコード(プラットフォームに依存)によって決まります。各演算子のためにマイクロベンチマークをしましょう。

ベンチマーク

クレジット:@ aleksandr-dubinskyのアドバイスに従って、そしてベースコードを提供してくれた@yuraのおかげで、これが JMH ベンチマークです(これを見てください チューニングガイド ):

class A {}
class B extends A {}

public class Benchmark {

    public static final Object a = new A();
    public static final Object b = new B();

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testInstanceOf()
    {
        return b instanceof A;
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testIsInstance()
    {
        return A.class.isInstance(b);
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testIsAssignableFrom()
    {
        return A.class.isAssignableFrom(b.getClass());
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(TestPerf2.class.getSimpleName())
                .warmupIterations(20)
                .measurementIterations(2000)
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

次の結果を出します(スコアは時間単位の操作数です。スコアが高いほど良いです)。

Benchmark                       Mode   Cnt    Score   Error   Units
Benchmark.testIsInstance        thrpt  2000  373,061 ± 0,115  ops/us
Benchmark.testInstanceOf        thrpt  2000  371,047 ± 0,131  ops/us
Benchmark.testIsAssignableFrom  thrpt  2000  363,648 ± 0,289  ops/us

警告

  • ベンチマークはJVMとプラットフォームに依存します。各操作の間に大きな違いはないので、異なるJavaバージョンやSolaris、Mac、Linuxなどのプラットフォームでは、異なる結果(そしておそらく異なる順序で!)が得られる可能性があります。
  • ベンチマークは、「BがAを直接拡張する」場合の「is BはAのインスタンス」のパフォーマンスを比較します。クラス階層がより深く、より複雑な場合(BがXを拡張し、Yを拡張し、Zを拡張し、Aを拡張するなど)、結果は異なる可能性があります。
  • 通常は、まず演算子の1つを選択して(最も便利な)コードを記述してから、パフォーマンスのボトルネックがないかどうかを確認するためにコードをプロファイルすることをお勧めします。たぶん、この演算子はあなたのコードの文脈では無視できるでしょう、あるいは多分...
  • 前の点に関して、あなたのコードの文脈でのinstanceofisInstanceより簡単に最適化されるかもしれません。

例を挙げて説明すると、次のループを取ります。

class A{}
class B extends A{}

A b = new B();

boolean execute(){
  return A.class.isAssignableFrom(b.getClass());
  // return A.class.isInstance(b);
  // return b instanceof A;
}

// Warmup the code
for (int i = 0; i < 100; ++i)
  execute();

// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
   execute();
}
final long elapsed = System.nanoTime() - start;

JITのおかげで、コードはある時点で最適化され、次のようになりました。

  • instanceof:6ミリ秒
  • isInstance:12ミリ秒
  • isAssignableから:15ms

注意

もともとこの記事では、生のJavaでforループを使用して独自のベンチマークを行っていました。そのため、それは主にJITコンパイラがループを最適化するのにどれくらいの時間を要したかを測定することでした。詳細については、 繰り返し回数に依存しないパフォーマンステスト を参照してください。

関連する質問

106
JBE

a instanceof Bともっと直接的に等価なものは

B.class.isInstance(a)

anullでもある場合、これは機能します(falseを返します)。

33
user102008

上記の基本的な違いとは別に、Classのinstanceof演算子とisAssignableFromメソッドの間には、コアとなるわずかな違いがあります。

instanceofを「これ(左側の部分)thisまたはこのサブクラスのインスタンス(右側の部分)である」と読み、x.getClass().isAssignableFrom(Y.class)を「X x = new Y()を書くことができるか」と読みます。言い換えれば、instanceof演算子は左のオブジェクトが同じか右のクラスのサブクラスかをチェックし、isAssignableFromはメソッドが呼ばれたクラスの参照にパラメータクラスのオブジェクト(from)を代入できるかをチェックします。
これらはどちらも参照型ではなく実際のインスタンスを考慮していることに注意してください。

CがBを拡張し、BがAを拡張する3つのクラスA、B、Cの例を考えてみましょう。

B b = new C();

System.out.println(b instanceof A); //is b (which is actually class C object) instance of A, yes. This will return true.  
System.out.println(b instanceof B); // is b (which is actually class C object) instance of B, yes. This will return true.  
System.out.println(b instanceof C); // is b (which is actually class C object) instance of C, yes. This will return true. If the first statement would be B b = new B(), this would have been false.
System.out.println(b.getClass().isAssignableFrom(A.class));//Can I write C c = new A(), no. So this is false.
System.out.println(b.getClass().isAssignableFrom(B.class)); //Can I write C c = new B(), no. So this is false.
System.out.println(b.getClass().isAssignableFrom(C.class)); //Can I write C c = new C(), Yes. So this is true.
22
Ashish Arya

他にも違いがあります。

xがnullであれば、Xのnullインスタンスはfalseです。

null.getClass()。isAssignableFrom(X)はNullPointerExceptionをスローします。

15
S. Ali Tokmen

さらに別の違いがあります。テストするタイプ(Class)が動的な場合、メソッドのパラメータとして渡されると、instanceofはそれをカットしません。

boolean test(Class clazz) {
   return (this instanceof clazz); // clazz cannot be resolved to a type.
}

しかし、あなたはできる:

boolean test(Class clazz) {
   return (clazz.isAssignableFrom(this.getClass())); // okidoki
}

おっと、私はこの答えがすでにカバーされているのを見ます。たぶん、この例は誰かに役立つでしょう。

11
tkalmijn

このスレッドはinstanceofisAssignableFromとどう違うのかについての洞察を私に提供してくれたので、私は自分のものを共有したいと思いました。

私はisAssignableFromを使用することで、あるクラスの参照が別のインスタンスを取得できるかどうかを判断するための唯一の(おそらく唯一の方法ではないがおそらく最も簡単な)方法を見つけることができました。

したがって、クラスの1つからインスタンスを作成することを考えない限り、私がすべてクラスであったときに代入可能性を比較するためにinstanceof演算子を使用するのは良い考えではありませんでした。私はこれがずさんだと思いました。

7
Owen

次のような状況を検討してください。型Aがobj型のスーパークラスかどうかを調べたいとします。

... A.class.isAssignableFrom(obj.getClass())...

OR

... obj instanceof A ...

しかしisAssignableFromソリューションでは、objのタイプがここに表示されることが必要です。そうでない場合(例えば、objのタイプがプライベート内部クラスの場合がある)、このオプションは無効です。ただし、instanceofソリューションは常に機能します。

4
algebra

instanceofは、プリミティブ型またはジェネリック型とも使用できません。次のコードのように:

//Define Class< T > type ... 

Object e = new Object();

if(e instanceof T) {
  // Do something.
}

エラーは次のとおりです。型パラメータTに対してinstanceofチェックを実行できません。実行時にさらに一般型情報が消去されるため、代わりに消去オブジェクトを使用してください。

型の消去によりランタイム参照が削除されたためコンパイルされません。ただし、以下のコードはコンパイルされます。

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
3
James Drinkard
isAssignableFrom(A, B) =

if (A == B) return true
else if (B == Java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))

上記の擬似コードは、型/クラスAの参照が型/クラスBの参照から代入可能であれば、の定義です。これは再帰的な定義です。ある人にとっては役に立つかもしれませんし、他の人にとっては混乱するかもしれません。誰かがそれを役に立つと思うならば私はそれを加えます。これは私の理解を捉えるための単なる試みであり、公式の定義ではありません。これは特定のJava VM実装で使用され、多くのプログラム例で機能します。したがって、isAssignableFromのすべての側面を捉えることは保証できませんが、完全には無効になっていません。

0

パフォーマンス「2」で話す(JMHと):

class A{}
class B extends A{}

public class InstanceOfTest {

public static final Object a = new A();
public static final Object b = new B();

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean testInstanceOf()
{
    return b instanceof A;
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean testIsInstance()
{
    return A.class.isInstance(b);
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean testIsAssignableFrom()
{
    return A.class.isAssignableFrom(b.getClass());
}

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(InstanceOfTest.class.getSimpleName())
            .warmupIterations(5)
            .measurementIterations(5)
            .forks(1)
            .build();

    new Runner(opt).run();
}
}

それは与えます:

Benchmark                            Mode  Cnt  Score   Error  Units
InstanceOfTest.testInstanceOf        avgt    5  1,972 ? 0,002  ns/op
InstanceOfTest.testIsAssignableFrom  avgt    5  1,991 ? 0,004  ns/op
InstanceOfTest.testIsInstance        avgt    5  1,972 ? 0,003  ns/op

つまり、instanceofisInstance()と同じくらい速く終了できます。 isAssignableFrom()それほど遠くない(+ 0.9%実行時間)。だから、あなたが選んだものは何でも本当の違いはありません

0
Yura