Apa perbedaan antara instanceof dan Class.isAssignableFrom (...)?


458

Manakah dari berikut ini yang lebih baik?

a instanceof B

atau

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

Satu-satunya perbedaan yang saya tahu adalah, ketika 'a' adalah null, yang pertama mengembalikan false, sedangkan yang kedua melempar pengecualian. Selain itu, apakah mereka selalu memberikan hasil yang sama?


17
Sebagai catatan, isInstance () adalah metode yang paling mudah untuk memeriksa apakah suatu objek dapat dimasukkan ke dalam tipe kelas (untuk lebih jelasnya, lihat: tshikatshikaaa.blogspot.nl/2012/07/… )
Jérôme Verstrynge

Jawaban:


498

Saat menggunakan instanceof, Anda perlu mengetahui kelas Bpada waktu kompilasi. Saat menggunakannya isAssignableFrom()bisa dinamis dan berubah saat runtime.


12
saya tidak mengerti - tolong jelaskan mengapa kita tidak bisa menulis a instanceof Bref.getClass(). bagaimana ini bisa menjadi jawaban yang diterima dengan sedikit penjelasan (atau ketiadaannya)?
Eliran Malka

65
Sintaksnya a instanceof Breftidak a instanceof Bref.class. Argumen kedua ke instance dari operator adalah nama kelas, bukan ekspresi yang menyelesaikan ke instance objek kelas.
Brandon Bloom

2
ya "dinamis" tidak perlu dikatakan :) Selain kinerja, ini adalah perbedaan yang sebenarnya.
peterk

2
@ EliranMalka mungkin Anda dapat memiliki kelas yang dibuat saat runtime. Seperti objek proxy.
Wagner Tsuchiya

Jadi, dalam B.class.isAssignableFrom(a.getClass()), B dikenal, dan a instanceof Blebih baik. Baik?
Florian F


116

Berbicara dalam hal kinerja:

TL; DR

Gunakan Instance atau instanceof yang memiliki kinerja serupa. isAssignableFrom sedikit lebih lambat.

Diurutkan berdasarkan kinerja:

  1. isInstance
  2. instanceof (+ 0,5%)
  3. isAssignableFrom (+ 2,7%)

Berdasarkan tolok ukur 2000 iterasi pada JAVA 8 Windows x64, dengan 20 iterasi pemanasan.

Dalam teori

Dengan menggunakan bytecode viewer yang lembut, kita dapat menerjemahkan masing-masing operator menjadi bytecode.

Dalam konteks:

package foo;

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

  ...

}

JAWA:

b instanceof A;

Bytecode:

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

JAWA:

A.class.isInstance(b);

Bytecode:

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

JAWA:

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

Bytecode:

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);

Mengukur berapa banyak instruksi bytecode yang digunakan oleh masing-masing operator, kita dapat mengharapkan instance dan Instance menjadi lebih cepat daripada IssignableFrom . Namun, kinerja aktual TIDAK ditentukan oleh bytecode tetapi oleh kode mesin (yang bergantung pada platform). Mari kita lakukan patokan mikro untuk masing-masing operator.

Patokan

Kredit: Seperti yang disarankan oleh @ aleksandr-dubinsky, dan terima kasih kepada @yura untuk memberikan kode dasar, berikut ini adalah patokan JMH (lihat panduan tuning ini ):

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();
    }
}

Memberi hasil berikut (skor adalah sejumlah operasi dalam satuan waktu , sehingga semakin tinggi skor semakin baik):

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

Peringatan

  • patokannya adalah JVM dan tergantung platform. Karena tidak ada perbedaan signifikan antara setiap operasi, dimungkinkan untuk mendapatkan hasil yang berbeda (dan mungkin urutan yang berbeda!) Pada versi JAVA yang berbeda dan / atau platform seperti Solaris, Mac atau Linux.
  • tolok ukur membandingkan kinerja "adalah B sebuah instance dari A" ketika "B meluas A" secara langsung. Jika hierarki kelas lebih dalam dan lebih kompleks (seperti B memanjang X yang memanjang Y yang memanjang Z yang memanjang A), hasilnya mungkin berbeda.
  • biasanya disarankan untuk menulis kode terlebih dahulu memilih salah satu operator (yang paling nyaman) dan kemudian membuat profil kode Anda untuk memeriksa apakah ada hambatan kinerja. Mungkin operator ini dapat diabaikan dalam konteks kode Anda, atau mungkin ...
  • dalam kaitannya dengan poin sebelumnya, instanceofdalam konteks kode Anda mungkin dioptimalkan lebih mudah daripada isInstancemisalnya ...

Untuk memberi Anda contoh, ambil loop berikut:

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;

Berkat JIT, kode ini dioptimalkan pada beberapa titik dan kami mendapatkan:

  • instanceof: 6ms
  • isInstance: 12ms
  • isAssignableFrom: 15ms

Catatan

Awalnya posting ini sedang melakukan benchmark sendiri menggunakan for loop di JAWA mentah, yang memberikan hasil tidak dapat diandalkan karena beberapa optimasi seperti Just In Time dapat menghilangkan loop. Jadi sebagian besar mengukur berapa lama yang dibutuhkan kompiler JIT untuk mengoptimalkan loop: lihat Tes kinerja terlepas dari jumlah iterasi untuk lebih jelasnya

Pertanyaan-pertanyaan Terkait


6
Yap, instanceofadalah bytecode yang pada dasarnya menggunakan logika yang sama dengan checkcast(bytecode di belakang casting). Secara inheren akan lebih cepat daripada opsi lain, terlepas dari tingkat optimasi JITC.
Hot Licks

1
Yang masuk akal, seperti isAssignableFrom()dinamis.
Matthieu

ya, dengan hasil JMH sama sekali berbeda (kecepatan yang sama untuk semua).
Yura

Hai, tolok ukur yang bagus, baru saja mengalami situasi di mana IssignableFrom dipanggil ribuan kali, berubah menjadi contoh benar-benar membuat perbedaan. Balasan ini akan layak untuk posting blog di suatu tempat ...;)
Martin

33

Setara dengan lebih langsung a instanceof Badalah

B.class.isInstance(a)

Karya ini (kembali palsu) saat aini nulljuga.


Keren, tetapi ini tidak menjawab pertanyaan dan seharusnya menjadi komentar.
Madbreaks

23

Terlepas dari perbedaan dasar yang disebutkan di atas, ada perbedaan halus inti antara instanceof operator dan metodeAssignableFrom di Kelas.

Baca instanceofsebagai "apakah ini (bagian kiri) adalah contoh dari ini atau setiap subkelas dari ini (bagian kanan)" dan dibaca x.getClass().isAssignableFrom(Y.class)sebagai "Dapatkah saya menulis X x = new Y()". Dengan kata lain, instanceof operator memeriksa apakah objek kiri sama atau subkelas dari kelas kanan, sementara isAssignableFrommemeriksa apakah kita dapat menetapkan objek dari kelas parameter (dari) ke referensi kelas di mana metode ini dipanggil.
Perhatikan bahwa keduanya menganggap instance aktual bukan tipe referensi.

Pertimbangkan contoh 3 kelas A, B dan C di mana C meluas B dan B meluas A.

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.

3
b instanceof Asetara dengan A.class.isAssignableFrom(b.getClass())(seperti OP perhatikan). Teladan Anda benar tetapi tidak relevan.
Karu

Karena new Y()mungkin tidak legal jika Yabstrak atau tanpa konstruktor default publik, Anda dapat mengatakan X x = (Y)nulllegal jika dan hanya jika x.getClass().isAssignableFrom(Y.class)benar.
Earth Engine

2
Mengapa 'b.getClass (). IsAssignableFrom (A.class)' dalam contoh ini? Saya kira contoh harus dibalik A.class.isAssignableFrom (b.getClass ()).
loshad vtapkah

14

Ada juga perbedaan lain:

null instanceof X falsetidak peduli apa X

null.getClass (). isAssignableFrom (X) akan melempar NullPointerException


4
-1, salah: null instanceof X(di mana X adalah beberapa kelas yang dikenal pada waktu kompilasi) akan selalu kembali false.
Caspar

4
@ Kaspar sementara Anda benar, ide dasar adalah poin yang bagus. Saya mengedit posting sehingga benar.
erickson

1
ini membantu, tepi kasus selalu penting :).
triliunan

Untuk menjadi setara dengan baris pertama, seharusnya baris kedua X.class.isAssignableFrom(null.getClass())seharusnya tidak? Tapi ya, memanggil getClass()referensi nol akan menghasilkan NPE.
William Price

Jawaban ini melewatkan poin - dereferensi nol tidak relevan karena kegagalan terjadi di luar operasi (Anda selalu perlu memeriksa nol sebelum Anda menggunakan referensi seperti itu). Secara umum getClass()tidak boleh digunakan dengan isAssignableFromdi tempat pertama - operasi ini dimaksudkan untuk situasi yang tidak memiliki objek. Jika Anda memiliki referensi obyek a, penggunaan a instanceof SomeClass(jika Anda melakukan tahu jenis SomeClass) atau someObject.getClass().isInstance(a)(jika Anda tidak tahu jenis someObject).
AndrewF

12

Masih ada perbedaan lain. Jika tipe (Kelas) yang diuji melawan bersifat dinamis, misal diteruskan sebagai parameter metode, maka instanceof tidak akan memotongnya untuk Anda.

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

tetapi Anda dapat melakukannya:

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

Ups, saya melihat jawaban ini sudah dibahas. Mungkin contoh ini bermanfaat bagi seseorang.


3
sebenarnya tidak ada jawaban yang benar-benar benar. Tugas dari kelas, Class.isInstance adalah analog dari 'instanceof'
bestsss

Untuk memasukkan komentar yang benar @ bestsss ke dalam kode konkret: Karena Anda memiliki objek ( this), clazz.isInstance(this)akan lebih baik dalam contoh Anda.
AndrewF

7

Utas ini memberi saya beberapa wawasan tentang instanceofperbedaannya isAssignableFrom, jadi saya pikir saya akan membagikan sesuatu milik saya sendiri.

Saya telah menemukan bahwa menggunakan isAssignableFrommenjadi satu-satunya (mungkin bukan satu-satunya, tetapi mungkin cara termudah) untuk bertanya pada diri sendiri apakah referensi dari satu kelas dapat mengambil contoh dari yang lain, ketika seseorang memiliki contoh dari kedua kelas untuk melakukan perbandingan.

Oleh karena itu, saya tidak menemukan menggunakan instanceofoperator untuk membandingkan penugasan untuk menjadi ide yang baik ketika semua yang saya miliki adalah kelas, kecuali saya berpikir untuk membuat instance dari salah satu kelas; Saya pikir ini akan menjadi ceroboh.


5

instanceof tidak dapat digunakan dengan tipe primitif atau tipe generik. Seperti dalam kode berikut:

//Define Class< T > type ... 

Object e = new Object();

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

Kesalahannya adalah: Tidak dapat melakukan instanceof terhadap parameter tipe T. Gunakan objek penghapusan itu sebagai gantinya karena informasi tipe umum lebih lanjut akan dihapus saat runtime.

Tidak dikompilasi karena penghapusan tipe menghapus referensi runtime. Namun, kode di bawah ini akan mengkompilasi:

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}

4

Pertimbangkan situasi berikut. Misalkan Anda ingin memeriksa apakah tipe A adalah kelas super dari jenis obj, Anda dapat memilih salah satu

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

ATAU

... obj contoh dari A ...

Tetapi solusi isAssignableFrom mengharuskan jenis objek terlihat di sini. Jika ini bukan masalahnya (misalnya, jenis keberatan mungkin dari kelas dalam pribadi), opsi ini keluar. Namun, solusi instance akan selalu bekerja.


2
Itu tidak benar. Silakan lihat "Adam Rosenfield" komentar stackoverflow.com/questions/496928/…
Maxim Veksler

1
Bisakah Anda menguraikan "Itu tidak benar"? Komentar yang Anda lihat tidak ada hubungannya dengan skenario di posting saya. Saya punya beberapa kode tes yang mendukung penjelasan saya.
aljabar

Jika Anda memiliki referensi non-null ke instance objek ( objdalam contoh ini) dari jenis apa pun maka Anda dapat memanggil getClass()metode publik di atasnya untuk mendapatkan metadata refleksi untuk kelas pelaksana. Ini benar bahkan jika tipe kelas implementasi itu tidak akan terlihat secara hukum di lokasi itu pada waktu kompilasi. Tidak apa-apa saat runtime karena, bagi Anda untuk memegang objreferensi, beberapa jalur kode yang akhirnya memang memiliki akses legal ke kelas yang dibuat dan memberikan (bocor?) Kepada Anda.
William Price

0
isAssignableFrom(A, B) =

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

Kode pseudo di atas adalah definisi, jika referensi tipe / kelas A dapat diberikan dari referensi tipe / kelas B. Ini adalah definisi rekursif. Bagi sebagian orang mungkin bermanfaat, bagi yang lain mungkin membingungkan. Saya menambahkannya kalau-kalau ada yang merasa berguna. Ini hanya upaya untuk menangkap pemahaman saya, itu bukan definisi resmi. Ini digunakan dalam implementasi Java VM tertentu dan berfungsi untuk banyak contoh program, jadi sementara saya tidak dapat menjamin bahwa ia menangkap semua aspek isAssignableFrom, itu tidak sepenuhnya mati.


2
Tolong jelaskan apa yang kode ini lakukan dan bagaimana ia menjawab pertanyaan itu.
Dana Gugatan Monica

0

Berbicara dalam hal kinerja "2" (dengan 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();
}
}

Memberikan:

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

Sehingga kita dapat menyimpulkan: instanceof secepat isinstance () dan isAssignableFrom () tidak jauh (+ 0,9% waktu executon). Jadi tidak ada perbedaan nyata apa pun yang Anda pilih


0

Bagaimana dengan beberapa contoh untuk menunjukkannya dalam aksi ...

@Test
public void isInstanceOf() {
    Exception anEx1 = new Exception("ex");
    Exception anEx2 = new RuntimeException("ex");
    RuntimeException anEx3 = new RuntimeException("ex");

    //Base case, handles inheritance
    Assert.assertTrue(anEx1 instanceof Exception);
    Assert.assertTrue(anEx2 instanceof Exception);
    Assert.assertTrue(anEx3 instanceof Exception);

    //Other cases
    Assert.assertFalse(anEx1 instanceof RuntimeException);
    Assert.assertTrue(anEx2 instanceof RuntimeException);
    Assert.assertTrue(anEx3 instanceof RuntimeException);
}

@Test
public void isAssignableFrom() {
    Exception anEx1 = new Exception("ex");
    Exception anEx2 = new RuntimeException("ex");
    RuntimeException anEx3 = new RuntimeException("ex");

    //Correct usage = The base class goes first
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx1.getClass()));
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx2.getClass()));
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx3.getClass()));

    //Incorrect usage = Method parameter is used in the wrong order
    Assert.assertTrue(anEx1.getClass().isAssignableFrom(Exception.class));
    Assert.assertFalse(anEx2.getClass().isAssignableFrom(Exception.class));
    Assert.assertFalse(anEx3.getClass().isAssignableFrom(Exception.class));
}

-2

beberapa tes yang kami lakukan di tim kami menunjukkan bahwa A.class.isAssignableFrom(B.getClass())bekerja lebih cepat daripada B instanceof A. ini bisa sangat berguna jika Anda perlu memeriksa ini pada sejumlah besar elemen.


13
Hm, jika Anda memiliki hambatan instanceof, saya percaya Anda memiliki masalah desain yang serius ...
sleske

1
Jawaban oleh JBE menyajikan hipotesis yang berbeda dari hipotesis Anda.
Alastor Moody
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.