2009-01-30 38 views
389

Điều nào sau đây tốt hơn?Sự khác biệt giữa instanceof và Class.isAssignableFrom (...) là gì?

a instanceof B 

hoặc

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

Sự khác biệt duy nhất mà tôi biết là, khi 'a' là null, lợi nhuận đầu tiên sai sự thật, trong khi ném thứ hai một ngoại lệ. Ngoài ra, họ có luôn đưa ra kết quả tương tự không?

+13

Đối với các bản ghi, isInstance() là phương pháp thuận tiện nhất để kiểm tra xem một đối tượng có thể được đúc thành loại lớp hay không (để biết thêm chi tiết, xem: http://tshikatshikaaa.blogspot.nl /2012/07/java-instanceof-isassignablefrom-or.html) – JVerstry

Trả lời

421

Khi sử dụng instanceof, bạn cần biết lớp học B lúc biên dịch. Khi sử dụng isAssignableFrom(), nó có thể động và thay đổi trong thời gian chạy.

+10

tôi không hiểu - hãy xây dựng trên ** tại sao ** chúng ta không thể viết 'instanceof Bref.getClass()'. làm thế nào điều này có thể là câu trả lời được chấp nhận với rất ít lời giải thích (hoặc thiếu của nó)? –

+51

Cú pháp là 'một instanceof Bref' không phải' một instanceof Bref.class'. Đối số thứ hai cho toán tử instanceof là một tên lớp, không phải là một biểu thức giải quyết một cá thể đối tượng lớp. –

+1

có "động" không cần nói :) Khác với hiệu suất, đây là sự khác biệt thực sự. – peterk

190

instanceof chỉ có thể được sử dụng với các loại tham chiếu, không được sử dụng với các loại nguyên thủy. isAssignableFrom() có thể được sử dụng với bất kỳ đối tượng lớp:

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

Xem http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class).

+2

Tôi không thấy điểm sử dụng instanceof/isAssignableFrom với các kiểu nguyên thủy. –

4

Cân nhắc tình huống sau. Giả sử bạn muốn kiểm tra xem loại A là một lớp siêu của các loại đối tượng, bạn có thể đi một trong hai

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

HOẶC

... obj instanceof Một ...

Nhưng giải pháp isAssignableFrom đòi hỏi rằng các loại obj được hiển thị ở đây. Nếu đây không phải là trường hợp (ví dụ: loại obj có thể thuộc lớp bên trong riêng), tùy chọn này đã hết. Tuy nhiên, giải pháp instanceof sẽ luôn hoạt động.

+2

Điều đó không đúng. Vui lòng xem nhận xét "Adam Rosenfield" http://stackoverflow.com/questions/496928/what-is-the-difference-between-instanceof-and-class-isassignablefrom/496968#496968 –

+1

Bạn có thể giải thích "Điều đó không đúng" ? Nhận xét bạn tham chiếu không liên quan gì đến kịch bản trong bài đăng của tôi. Tôi có một số mã kiểm tra sao lưu lời giải thích của tôi. – algebra

+0

Nếu bạn có tham chiếu không null đối với một cá thể đối tượng ('obj' trong ví dụ này) của _any type_ thì bạn có thể gọi phương thức' getClass() 'công khai để lấy siêu dữ liệu phản chiếu cho lớp thực hiện. Điều này đúng ngay cả khi việc triển khai loại lớp sẽ không được hiển thị hợp pháp tại vị trí đó tại thời gian biên dịch. Đó là OK khi chạy vì, để bạn giữ tham chiếu 'obj', một số đường dẫn mã mà cuối cùng _did_ có quyền truy cập hợp pháp vào lớp đã tạo và cho (bị rò rỉ?) Nó cho bạn. –

-2

một số thử nghiệm mà chúng tôi đã làm trong nhóm của chúng tôi cho thấy rằng A.class.isAssignableFrom(B.getClass()) hoạt động nhanh hơn B instanceof A. điều này có thể rất hữu ích nếu bạn cần kiểm tra điều này trên số lượng lớn các phần tử.

+13

Hm, nếu bạn có một nút cổ chai trong 'instanceof', tôi tin rằng bạn có vấn đề thiết kế nghiêm trọng ... – sleske

+0

Câu trả lời của JBE trình bày giả thuyết khác với giả thuyết của bạn. –

23

Ngoài sự khác biệt cơ bản được đề cập ở trên, có một sự khác biệt tinh tế cốt lõi giữa toán tử instanceof và phương thức isAssignableFrom trong lớp.

đọc instanceof như “là thế này (phần bên trái) trường hợp của này hay bất kỳ lớp con của này (phần bên phải)” và đọc x.getClass().isAssignableFrom(Y.class) là “Tôi có thể viết X x = new Y()”. Nói cách khác, toán tử instanceof kiểm tra nếu đối tượng bên trái là cùng hoặc lớp con của lớp bên phải, trong khi kiểm tra isAssignableFrom nếu chúng ta có thể gán đối tượng của lớp tham số (từ) đến tham chiếu của lớp mà phương thức được gọi.
Lưu ý rằng cả hai trường hợp này đều coi trường hợp thực tế không phải là loại tham chiếu.

Hãy xem xét một ví dụ về 3 lớp A, B và C trong đó C mở rộng B và B kéo dài 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. 
+2

Bạn đã hiểu sai, xem mã này http://pastebin.com/tfCFs0QY –

+3

'b instanceof A' tương đương với' A.class.isAssignableFrom (b.getClass()) '(như OP được chú ý). Ví dụ của bạn là chính xác nhưng không liên quan. – Karu

+0

Vì 'new Y()' có thể không hợp lệ nếu 'Y' trừu tượng hoặc không có hàm dựng mặc định công khai, bạn có thể nói' X x = (Y) null' là hợp pháp nếu và chỉ khi 'x.getClass(). IsAssignableFrom (Y.class) 'là đúng. –

14

Ngoài ra còn có một sự khác biệt:

rỗng instanceof X là false không có vấn đề gì X là

null.getClass().isAssignableFrom (X) sẽ ném NullPointerException

+4

-1, không chính xác: 'null instanceof X' (trong đó X là một số lớp được biết tại thời gian biên dịch) sẽ luôn trả về' false'. – Caspar

+4

@Caspar trong khi bạn chính xác, ý tưởng cơ bản là một điểm tốt. Tôi đã chỉnh sửa bài đăng để nó đúng. – erickson

+1

điều này hữu ích, trường hợp cạnh luôn quan trọng :). – trillions

9

Vẫn còn một sự khác biệt. Nếu loại (Lớp) để kiểm tra là động, ví dụ: được thông qua như một tham số phương thức, sau đó instanceof sẽ không cắt nó cho bạn.

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

nhưng bạn có thể làm:

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

Rất tiếc, tôi thấy câu trả lời này đã được bảo hiểm. Có lẽ ví dụ này hữu ích cho ai đó.

+2

thực sự không có câu trả lời là thực sự chính xác isAssignableTừ công việc w/lớp học, Class.isInstance là tương tự của * 'instanceof' * – bestsss

33

Một tương đương trực tiếp hơn để a instanceof B

B.class.isInstance(a) 

này hoạt động (trả về false) khi anull quá.

7

Chủ đề này đã cung cấp cho tôi một số thông tin chi tiết về cách instanceof khác với isAssignableFrom, vì vậy tôi nghĩ mình sẽ chia sẻ điều gì đó của riêng mình.

Tôi đã thấy rằng việc sử dụng isAssignableFrom là cách duy nhất (có thể không phải là duy nhất, nhưng có thể dễ nhất) để tự hỏi bản thân nếu tham chiếu một lớp có thể lấy trường hợp khác. so sánh. Vì vậy, tôi không tìm thấy bằng cách sử dụng toán tử instanceof để so sánh khả năng gán là một ý tưởng hay khi tất cả những gì tôi có là các lớp, trừ khi tôi dự tính tạo một cá thể từ một trong các lớp; Tôi nghĩ điều này sẽ cẩu thả.

87

Nói về hiệu suất:

TL; DR

Sử dụng isinstance hoặc instanceof mà có hiệu suất tương tự. isAssignableFrom hơi chậm.

Sắp xếp theo hiệu suất:

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

Dựa trên một chuẩn mực của 2000 lần lặp lại trên JAVA 8 Windows x64, với 20 lần lặp lại.

Về lý thuyết

Sử dụng một phần mềm như bytecode viewer chúng ta có thể dịch mỗi nhà khai thác vào bytecode.

Trong bối cảnh:

package foo; 

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

    ... 

} 

JAVA:

b instanceof A; 

Bytecode:

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

JAVA:

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

JAVA:

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

đo bao nhiêu hướng dẫn bytecode được sử dụng bởi mỗi nhà khai thác, chúng ta có thể mong đợi instanceofisinstance để nhanh hơn isAssignableFrom. Tuy nhiên, hiệu suất thực tế KHÔNG được xác định bởi bytecode mà bởi mã máy (phụ thuộc vào nền tảng). Hãy làm một tiêu chuẩn vi mô cho mỗi nhà điều hành.

Điểm chuẩn

tín dụng: Là cố vấn bởi @ Aleksandr-Dubinsky, và nhờ @yura cho việc cung cấp các mã cơ bản, đây là một chuẩn mực JMH (xem này tuning guide):

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

Đưa ra các kết quả sau (điểm số là một số thao tác trong một đơn vị thời gian, vì vậy điểm càng cao càng tốt):

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 

Warning

  • điểm chuẩn là JVM và phụ thuộc nền tảng. Vì không có sự khác biệt đáng kể giữa mỗi hoạt động, có thể có được kết quả khác nhau (và có thể thứ tự khác!) Trên một phiên bản và/hoặc nền tảng JAVA khác như Solaris, Mac hoặc Linux.
  • điểm chuẩn so sánh hiệu suất của "là B một thể hiện của A" khi "B mở rộng A" trực tiếp. Nếu phân cấp lớp sâu hơn và phức tạp hơn (như B mở rộng X mở rộng Y mở rộng Z mở rộng A), kết quả có thể khác.
  • thường được khuyên nên viết mã trước tiên hãy chọn một toán tử (thuận tiện nhất) và sau đó lập hồ sơ cho mã của bạn để kiểm tra xem có tắc nghẽn hiệu suất hay không. Có thể nhà điều hành này không đáng kể trong bối cảnh mã của bạn hoặc có thể ...
  • liên quan đến điểm trước đó, instanceof trong ngữ cảnh mã của bạn có thể được tối ưu hóa dễ dàng hơn ví dụ isInstance ...

Để cung cấp cho bạn một ví dụ, đi vòng lặp sau:

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; 

Nhờ JIT, các mã được tối ưu hóa tại một số điểm và chúng tôi nhận được:

  • instanceof: 6ms
  • isInstance: 12ms
  • isAssignableTừ: 15ms

Note

Nguyên bài này được làm chuẩn riêng của mình bằng cách sử dụng cho vòng lặp trong JAVA liệu, trong đó đã cho kết quả không đáng tin cậy như một số tối ưu hóa như Just In Time có thể loại bỏ các vòng lặp. Vì vậy, nó đã được chủ yếu là đo bao lâu đã làm trình biên dịch JIT làm để tối ưu hóa vòng lặp: xem Performance test independent of the number of iterations để biết thêm chi tiết

câu hỏi liên quan

+6

Yep, 'instanceof' là một bytecode sử dụng cơ bản cùng một logic như' checkcast' (bytecode đằng sau đúc) . Nó vốn sẽ nhanh hơn các tùy chọn khác, bất kể mức độ tối ưu hóa JITC. –

+5

Đừng viết các văn bản riêng của bạn! Sử dụng JMH. –

+1

Có nghĩa là, vì 'isAssignableFrom()' là động. – Matthieu

3

instanceof không thể được sử dụng với các kiểu hoặc các kiểu nguyên thủy các loại ic. Giống như trong đoạn mã sau:

//Define Class<T> type ... 

Object e = new Object(); 

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

Lỗi này là: Không thể thực hiện kiểm tra kiểu đối chiếu với thông số loại T. Thay vì xóa thông tin loại chung thì sẽ bị xóa khi chạy.

Không biên dịch do xóa loại xóa tham chiếu thời gian chạy. Tuy nhiên, mã dưới đây sẽ biên dịch:

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

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

Mã giả trên là một định nghĩa về, nếu tài liệu tham khảo của loại/lớp A là chuyển nhượng từ tài liệu tham khảo của loại/lớp B. Nó là một định nghĩa đệ quy. Đối với một số nó có thể hữu ích, cho những người khác nó có thể gây nhầm lẫn. Tôi thêm nó trong trường hợp ai đó nên tìm thấy nó hữu ích. Đây chỉ là một nỗ lực để nắm bắt được sự hiểu biết của tôi, nó không phải là định nghĩa chính thức. Nó được sử dụng trong một thực thi Java VM nhất định và làm việc cho nhiều chương trình ví dụ, vì vậy trong khi tôi không thể bảo đảm rằng nó nắm bắt tất cả các khía cạnh của isAssignableFrom, nó không hoàn toàn tắt.

+2

Vui lòng giải thích mã này làm gì và cách nó trả lời câu hỏi. –

0

Nói về hiệu suất "2" (với 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(); 
} 
} 

Nó cung cấp cho:

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 

Vì vậy mà chúng ta có thể kết luận: instanceof càng nhanh càng isinstance()isAssignableFrom() không xa (+ 0,9% thời gian thực hiện).Vì vậy, không có sự khác biệt thực sự bất cứ điều gì bạn chọn

Các vấn đề liên quan