ข้อใดต่อไปนี้ดีกว่า
a instanceof B
หรือ
B.class.isAssignableFrom(a.getClass())
ความแตกต่างเพียงอย่างเดียวที่ฉันรู้คือเมื่อ 'a' เป็นโมฆะอันแรกจะคืนค่าเท็จในขณะที่ตัวที่สองส่งข้อยกเว้น นอกจากนั้นพวกเขาจะให้ผลลัพธ์เดียวกันเสมอหรือไม่?
ข้อใดต่อไปนี้ดีกว่า
a instanceof B
หรือ
B.class.isAssignableFrom(a.getClass())
ความแตกต่างเพียงอย่างเดียวที่ฉันรู้คือเมื่อ 'a' เป็นโมฆะอันแรกจะคืนค่าเท็จในขณะที่ตัวที่สองส่งข้อยกเว้น นอกจากนั้นพวกเขาจะให้ผลลัพธ์เดียวกันเสมอหรือไม่?
คำตอบ:
เมื่อใช้instanceof
คุณจำเป็นต้องรู้ระดับของB
เวลารวบรวม เมื่อใช้isAssignableFrom()
มันสามารถเป็นแบบไดนามิกและเปลี่ยนแปลงระหว่างรันไทม์
a instanceof Bref.getClass()
เราไม่สามารถเขียน นี่เป็นคำตอบที่ได้รับการยอมรับได้อย่างไรพร้อมคำอธิบายเล็กน้อย (หรือคำขาด)
a instanceof Bref
a instanceof Bref.class
อาร์กิวเมนต์ที่สองของตัวดำเนินการ instanceof คือชื่อคลาสไม่ใช่นิพจน์ที่แก้ไขเป็นอินสแตนซ์อ็อบเจ็กต์คลาส
B.class.isAssignableFrom(a.getClass())
B จึงเป็นที่รู้จักและa instanceof B
ดีกว่า ขวา?
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)
การพูดคุยในแง่ของประสิทธิภาพ:
TL; DR
ใช้isInstanceหรือinstanceofที่มีประสิทธิภาพใกล้เคียงกัน isAssignable จากช้ากว่าเล็กน้อย
จัดเรียงตามประสิทธิภาพ:
อ้างอิงตามเกณฑ์มาตรฐานของการวนซ้ำ 2,000 รอบบน JAVA 8 Windows x64 พร้อมการวอร์มอัพซ้ำ 20 ครั้ง
ในทางทฤษฎี
การใช้เครื่องมือดูไบเทคโคเดอร์อ่อน ๆ ทำให้เราสามารถแปลโอเปอเรเตอร์แต่ละตัวเป็น bytecode
ในบริบทของ:
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);
การวัดจำนวนคำสั่ง bytecode ที่ใช้โดยผู้ให้บริการแต่ละรายเราสามารถคาดหวังอินสแตนซ์ของและisInstanceให้เร็วกว่าisAssableableจาก อย่างไรก็ตามประสิทธิภาพที่แท้จริงไม่ได้ถูกกำหนดโดย bytecode แต่ตามรหัสเครื่อง (ซึ่งขึ้นอยู่กับแพลตฟอร์ม) ลองทำเกณฑ์มาตรฐานขนาดเล็กสำหรับผู้ให้บริการแต่ละราย
เกณฑ์มาตรฐาน
เครดิต: ตามที่ได้รับคำแนะนำจาก @ 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
คำเตือน
instanceof
ในบริบทของรหัสของคุณอาจได้รับการปรับให้ง่ายกว่าisInstance
ตัวอย่างเช่น ...ในการให้ตัวอย่างให้ใช้ลูปต่อไปนี้:
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 รหัสนี้จึงได้รับการปรับปรุงและเราจะได้รับ:
บันทึก
แต่เดิมโพสต์นี้กำลังสร้างมาตรฐานของตัวเองโดยใช้for for loop ใน JAVA ดิบซึ่งให้ผลลัพธ์ที่ไม่น่าเชื่อถือเนื่องจากการเพิ่มประสิทธิภาพบางอย่างเช่น Just In Time สามารถกำจัดลูปได้ ดังนั้นจึงเป็นการวัดว่าคอมไพเลอร์ JIT ใช้เวลานานแค่ไหนในการปรับลูปให้เหมาะสม: ดูการทดสอบประสิทธิภาพโดยไม่ขึ้นกับจำนวนการวนซ้ำสำหรับรายละเอียดเพิ่มเติม
คำถามที่เกี่ยวข้อง
instanceof
เป็นไบต์ที่ใช้ตรรกะเดียวกันเป็นหลักcheckcast
(bytecode ที่อยู่เบื้องหลังการคัดเลือก) มันจะเร็วกว่าตัวเลือกอื่นโดยไม่คำนึงถึงระดับของการเพิ่มประสิทธิภาพ JITC
isAssignableFrom()
อยู่แบบไดนามิก
เทียบเท่าโดยตรงกับa instanceof B
คือ
B.class.isInstance(a)
วิธีนี้ใช้งานได้ (คืนค่าเท็จ) เมื่อa
เป็นnull
เช่นกัน
นอกเหนือจากความแตกต่างพื้นฐานดังกล่าวข้างต้นมีความแตกต่างที่สำคัญหลักระหว่างผู้ประกอบการของอินสแตนซ์และวิธี isAssignableFrom ในชั้นเรียน
อ่านinstanceof
ว่า“ นี่คือ (ส่วนซ้าย) ตัวอย่างของคลาสนี้หรือคลาสย่อยของส่วนนี้ (ส่วนที่เหมาะสม)” และอ่านx.getClass().isAssignableFrom(Y.class)
เป็น“ ฉันจะเขียนX x = new Y()
” ได้อย่างไร ในคำอื่น ๆ ผู้ประกอบการ instanceof ตรวจสอบว่าวัตถุซ้ายเหมือนกันหรือคลาสย่อยของคลาสที่เหมาะสมในขณะที่isAssignableFrom
ตรวจสอบว่าเราสามารถกำหนดวัตถุของคลาสพารามิเตอร์ (จาก) เพื่อการอ้างอิงของระดับที่วิธีการที่เรียกว่า
โปรดทราบว่าทั้งสองนี้พิจารณาอินสแตนซ์จริงไม่ใช่ประเภทการอ้างอิง
ลองพิจารณาตัวอย่างของ 3 คลาส A, B และ C โดยที่ C ขยาย B และ B ขยาย 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.
b instanceof A
เทียบเท่ากับA.class.isAssignableFrom(b.getClass())
(ตามที่ OP สังเกตเห็น) ตัวอย่างของคุณถูกต้อง แต่ไม่เกี่ยวข้อง
new Y()
อาจไม่ถูกกฎหมายหากY
เป็นนามธรรมหรือไม่มีตัวสร้างค่าเริ่มต้นสาธารณะคุณสามารถพูดได้ว่าX x = (Y)null
ถูกต้องตามกฎหมายถ้าหากx.getClass().isAssignableFrom(Y.class)
เป็นจริง
นอกจากนี้ยังมีข้อแตกต่างอื่น ๆ :
อินสแตนซ์ที่ว่างของ X false
ไม่สำคัญว่า X คืออะไร
null.getClass () isAssignableFrom (X) จะโยน NullPointerException
null instanceof X
(โดยที่ X เป็นชั้นบางที่รู้จักกันที่รวบรวมเวลา) false
มักจะกลับมา
X.class.isAssignableFrom(null.getClass())
อย่างไร แต่ใช่การเรียกgetClass()
อ้างอิงแบบ null จะส่งผลให้เกิด NPE
getClass()
ไม่ควรใช้กับisAssignableFrom
ในตอนแรก - การดำเนินการมีความหมายสำหรับสถานการณ์ที่ไม่มีวัตถุ หากคุณมีการอ้างอิงวัตถุa
ใช้a instanceof SomeClass
(ถ้าคุณทำทราบชนิดSomeClass
) หรือsomeObject.getClass().isInstance(a)
(ถ้าคุณไม่ทราบชนิดของsomeObject
)
ยังมีความแตกต่างอื่น หากประเภท (Class) ที่จะทดสอบเป็นแบบไดนามิกเช่นส่งผ่านเป็นพารามิเตอร์ method ดังนั้น 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
}
โอ๊ะฉันเห็นคำตอบนี้ครอบคลุมแล้ว บางทีตัวอย่างนี้อาจมีประโยชน์กับบางคน
this
) clazz.isInstance(this)
จะดีกว่าในตัวอย่างของคุณ
หัวข้อนี้ให้ฉันเข้าใจว่าinstanceof
แตกต่างจากisAssignableFrom
ดังนั้นฉันคิดว่าฉันจะแบ่งปันบางอย่างของตัวเอง
ฉันได้พบว่าการใช้ isAssignableFrom
เป็นวิธีเดียว (อาจไม่ใช่วิธีเดียว แต่อาจเป็นวิธีที่ง่ายที่สุด) ในการถามตนเองว่าการอ้างอิงของคลาสหนึ่งสามารถใช้อินสแตนซ์ของคลาสอื่นได้หรือไม่เมื่อมีอินสแตนซ์ของคลาสที่ไม่ทำการเปรียบเทียบ
ดังนั้นฉันไม่พบการใช้instanceof
โอเปอเรเตอร์เพื่อเปรียบเทียบความสามารถในการมอบหมายให้เป็นความคิดที่ดีเมื่อฉันมีคลาสทั้งหมดยกเว้นว่าฉันได้พิจารณาการสร้างอินสแตนซ์จากหนึ่งในคลาส ฉันคิดว่ามันจะเลอะเทอะ
instanceof ไม่สามารถใช้กับชนิดดั้งเดิมหรือประเภททั่วไปอย่างใดอย่างหนึ่ง ในรหัสต่อไปนี้:
//Define Class< T > type ...
Object e = new Object();
if(e instanceof T) {
// Do something.
}
ข้อผิดพลาดคือ: ไม่สามารถดำเนินการอินสแตนซ์ของการตรวจสอบกับพารามิเตอร์ประเภท T ใช้มันเป็นลบวัตถุแทนเนื่องจากข้อมูลประเภททั่วไปเพิ่มเติมจะถูกลบในขณะทำงาน
ไม่รวบรวมเนื่องจากการลบประเภทลบการอ้างอิงรันไทม์ อย่างไรก็ตามรหัสด้านล่างจะรวบรวม:
if( type.isAssignableFrom(e.getClass())){
// Do something.
}
พิจารณาสถานการณ์ต่อไปนี้ สมมติว่าคุณต้องการตรวจสอบว่า Type A เป็น class super ของประเภท obj หรือไม่คุณสามารถไปได้
... A.class.isAssignableFrom (obj.getClass ()) ...
หรือ
... obj instanceof A ...
แต่โซลูชัน isAssignableFrom ต้องการให้ชนิดของ obj สามารถมองเห็นได้ที่นี่ หากนี่ไม่ใช่กรณี (เช่นชนิดของ obj อาจเป็นคลาสส่วนตัวภายใน) ตัวเลือกนี้จะไม่ทำงาน อย่างไรก็ตามการแก้ปัญหาอินสแตนซ์ของจะทำงานเสมอ
obj
ในตัวอย่างนี้) ชนิดใด ๆจากนั้นคุณสามารถเรียกgetClass()
วิธีการสาธารณะที่จะได้รับเมตาดาต้าสะท้อนสำหรับการใช้งานคลาส สิ่งนี้เป็นจริงแม้ว่าการใช้ประเภทชั้นเรียนจะไม่สามารถมองเห็นได้ตามกฎหมาย ณ สถานที่นั้นในเวลารวบรวม ไม่เป็นไรตอนรันไทม์เพราะสำหรับคุณที่จะเก็บการobj
อ้างอิงบางเส้นทางรหัสที่ในที่สุดก็มีการเข้าถึงทางกฎหมายในชั้นเรียนที่สร้างขึ้นหนึ่งและให้ (รั่วไหลออกมา) ให้คุณ
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 บางอย่างและทำงานกับโปรแกรมตัวอย่างจำนวนมากดังนั้นในขณะที่ฉันไม่สามารถรับประกันได้ว่าจะจับภาพทุกด้านของ isAssignable จากมันไม่ได้ปิดอย่างสมบูรณ์
การพูดคุยในแง่ของประสิทธิภาพ "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
เพื่อให้เราสามารถสรุป: อินสแตนซ์ของเร็วเท่าisInstance ()และisAssignableFrom ()ไม่ไกล (+ 0.9% เวลาดำเนินการ) ดังนั้นจึงไม่มีความแตกต่างอย่างแท้จริงกับสิ่งที่คุณเลือก
ตัวอย่างเกี่ยวกับวิธีที่จะแสดงมันในการดำเนินการ ...
@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));
}
การทดสอบบางอย่างที่เราทำในการแสดงว่าทีมงานของเราทำงานได้เร็วกว่าA.class.isAssignableFrom(B.getClass())
B instanceof A
สิ่งนี้มีประโยชน์มากหากคุณต้องการตรวจสอบสิ่งนี้กับองค์ประกอบจำนวนมาก
instanceof
ผมเชื่อว่าคุณมีปัญหาในการออกแบบร้ายแรง ...