Aşağıdakilerden hangisi daha iyidir?
a instanceof B
veya
B.class.isAssignableFrom(a.getClass())
Bildiğim tek fark, 'a' boş olduğunda, ilki yanlış döndürürken ikincisi bir istisna atar. Bunun dışında hep aynı sonucu veriyorlar mı?
Aşağıdakilerden hangisi daha iyidir?
a instanceof B
veya
B.class.isAssignableFrom(a.getClass())
Bildiğim tek fark, 'a' boş olduğunda, ilki yanlış döndürürken ikincisi bir istisna atar. Bunun dışında hep aynı sonucu veriyorlar mı?
Yanıtlar:
Kullanırken instanceof
, B
derleme zamanında sınıfını bilmeniz gerekir . Kullanırken isAssignableFrom()
dinamik olabilir ve çalışma sırasında değişebilir.
a instanceof Bref.getClass()
. nasıl bu kadar az açıklamayla (ya da eksikliği) kabul edilen cevap nasıl olabilir?
a instanceof Bref
değil a instanceof Bref.class
. İnstanceof operatörünün ikinci argümanı, sınıf nesnesi örneğine çözümlenen bir ifade değil, bir sınıf adıdır.
B.class.isAssignableFrom(a.getClass())
, B bilinir ve a instanceof B
daha iyidir. Sağ?
instanceof
sadece referans tipleriyle kullanılabilir, ilkel tiplerle değil. isAssignableFrom()
herhangi bir sınıf nesnesiyle kullanılabilir:
a instanceof int // syntax error
3 instanceof Foo // syntax error
int.class.isAssignableFrom(int.class) // true
Görmek http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class) .
Performans açısından konuşmak:
TL; DR
Benzer performansa sahip isInstance veya instanceof kullanın . isAssignableFrom biraz daha yavaş.
Performansa göre sıralandı:
20 ısınma yinelemesi ile JAVA 8 Windows x64 üzerinde 2000 yinelemesinin bir karşılaştırmasına dayanmaktadır.
Teoride
Yumuşak bir bayt kodu görüntüleyici kullanarak her bir operatörü bayt koduna çevirebiliriz.
Bağlamında:
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);
Her bir operatör tarafından kaç bayt kod talimatı kullanıldığını ölçerek , instanceof ve isInstance öğelerinin isAssignableFrom'dan daha hızlı olmasını bekleyebiliriz . Ancak, gerçek performans bayt koduyla değil, makine koduyla (platforma bağımlıdır) belirlenir. Her operatör için bir mikro ölçüt yapalım.
Kıyaslamak
Kredi: @ aleksandr-dubinsky tarafından önerildiği gibi ve @yura'ya temel kodu sağladığı için teşekkürler, burada bir JMH karşılaştırması bulunmaktadır (bu ayarlama kılavuzuna bakın ):
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şağıdaki sonuçları verdi (puan bir zaman birimindeki bir dizi işlemdir , bu nedenle puan ne kadar yüksek olursa o kadar iyidir):
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
Uyarı
instanceof
ile ilgili olarak, kod bağlamında isInstance
örneğin daha kolay optimize edilebilir ...Size bir örnek vermek için aşağıdaki döngüyü uygulayın:
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 sayesinde kod bir noktada optimize edilmiştir ve şunu elde ederiz:
Not
Başlangıçta bu yazı, Ham JAVA'da bir for döngüsü kullanarak kendi karşılaştırmasını yapıyor , bu da Just In Time gibi bazı optimizasyon döngüyü ortadan kaldırabileceğinden güvenilir sonuçlar vermedi. Bu yüzden çoğunlukla JIT derleyicisinin döngüyü optimize etmek için ne kadar sürdüğünü ölçüyordu: daha fazla ayrıntı için yineleme sayısından bağımsız olarak Performans testine bakın
İlgili sorular
instanceof
esas olarak aynı mantığı kullanan bir bayt kodudur checkcast
(dökümün arkasındaki bayt kodu). JITC optimizasyon derecesi ne olursa olsun, diğer seçeneklerden daha hızlı olacaktır.
isAssignableFrom()
Dinamik olduğu gibi mantıklı .
A daha doğrudan eşdeğer a instanceof B
DİR
B.class.isInstance(a)
Bu da çalışır (false a
değerini döndürür) null
.
Yukarıda belirtilen temel farklılıkların yanı sıra, instanceof operatörü ve Sınıfta isAssignableFrom yöntemi arasında temel bir küçük fark vardır.
Oku instanceof
“Bu (sol kısmı) bu örneğini veya bu alt sınıflarının (sağ kısım)” olarak ve okumak x.getClass().isAssignableFrom(Y.class)
“Can I yazma olarak X x = new Y()
”. Başka bir deyişle, instanceof operatörü, sol nesnenin aynı veya sağ sınıfın alt sınıfında olup isAssignableFrom
olmadığını denetlerken, yöntem sınıfının (from) nesnesini yöntemin çağrıldığı sınıfın referansına atayabilip atamadığımızı denetler.
Her ikisinin de gerçek örneği referans türü olarak görmediğini unutmayın.
C'nin B ve B'nin A'yı genişlettiği 3 A, B ve C sınıfı örneğini ele alalım.
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
eşdeğerdir A.class.isAssignableFrom(b.getClass())
(OP'nin fark ettiği gibi). Örneğin doğru ama alakasız.
new Y()
eğer yasal olmayabilir Y
soyut veya kamu varsayılan yapıcı olmayan, sen söyleyebiliriz X x = (Y)null
ancak ve ancak yasal x.getClass().isAssignableFrom(Y.class)
doğrudur.
Başka bir fark daha var:
X'in değeri false
ne olursa olsun , X'in sıfır örneği
null.getClass (). isAssignableFrom (X) bir NullPointerException kurar
null instanceof X
(burada X, derleme zamanında bilinen bir sınıftır) her zaman geri döner false
.
X.class.isAssignableFrom(null.getClass())
mı? Ancak evet, getClass()
null referansı çağırmak NPE ile sonuçlanacaktır.
getClass()
olarak isAssignableFrom
ilk etapta kullanılmamalıdır - operasyon hiçbir nesnenin olmadığı durum içindir. Eğer nesne referansı varsa a
, kullanımı a instanceof SomeClass
(eğer varsa yapmak türünü bilmek SomeClass
) ya da someObject.getClass().isInstance(a)
(eğer varsa yok türünü bilmek someObject
).
Başka bir fark daha var. Test edilecek tür (Sınıf) dinamikse, örneğin bir yöntem parametresi olarak iletildiyse, instanceof bunu sizin için kesmez.
boolean test(Class clazz) {
return (this instanceof clazz); // clazz cannot be resolved to a type.
}
ama şunları yapabilirsiniz:
boolean test(Class clazz) {
return (clazz.isAssignableFrom(this.getClass())); // okidoki
}
Hata! Bu cevabın zaten kapsantığını görüyorum. Belki bu örnek birisi için yararlıdır.
this
) olduğundan, clazz.isInstance(this)
örneğinizde daha iyi olur.
Bu konu bana nasıl instanceof
farklı isAssignableFrom
olduğuna dair bir fikir verdi , bu yüzden kendime ait bir şey paylaşacağımı düşündüm.
isAssignableFrom
Bir karşılaştırma yapmak için her iki sınıfın örnekleri olduğunda, bir sınıfın bir referans başka bir örneği alabilir, kendi kendine sormak için tek (muhtemelen tek, ama muhtemelen en kolay) yolu olarak bulduk .
Bu nedenle, instanceof
sınıflardan birinden bir örnek oluşturmayı düşünmediğim sürece, sahip olduğum tek şey sınıflarken iyi bir fikir olarak karşılaştırılabilirliği karşılaştırmak için operatörü kullanmadım ; Bunun özensiz olacağını düşündüm.
instanceof, ilkel tiplerle veya jenerik tiplerle de kullanılamaz. Aşağıdaki kodda olduğu gibi:
//Define Class< T > type ...
Object e = new Object();
if(e instanceof T) {
// Do something.
}
Hata: T tür parametresine karşı instanceof kontrolü gerçekleştirilemiyor. Çalışma sırasında daha fazla genel tür bilgisi silineceğinden, bunun silme Nesnesini kullanın.
Çalışma zamanı başvurusunu kaldırarak tür silme nedeniyle derlenmez. Ancak, aşağıdaki kod derlenecektir:
if( type.isAssignableFrom(e.getClass())){
// Do something.
}
Aşağıdaki durumu göz önünde bulundurun. Diyelim ki A türünün obj türünün süper bir sınıfı olup olmadığını kontrol etmek istiyorsunuz, ya
... A.class.isAssignableFrom (obj.getClass ()) ...
VEYA
... obj A örneği ...
Ancak isAssignableFrom çözümü, obj türünün burada görünür olmasını gerektirir. Durum böyle değilse (örneğin, obj türü özel bir iç sınıftan olabilir), bu seçenek devre dışıdır. Ancak, çözüm örneği her zaman işe yarayacaktır.
obj
bu örnekte) null olmayan bir başvurunuz varsa, uygulayıcı sınıfın yansıma meta verilerini edinmek için genel yöntemi çağırabilirsiniz . Sınıf uygulayan sınıf türü derleme zamanında bu konumda yasal olarak görünmese bile bu doğrudur. Zamanında 's Tamam tutmak için, çünkü referansı, sonuçta bazı kod yolu yaptılar sınıfına yasal erişime sahip birini yaratan ve verdi (sızdırılmış?) Onu sana. getClass()
obj
isAssignableFrom(A, B) =
if (A == B) return true
else if (B == java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))
Yukarıdaki sözde kod, A tipi / A sınıfı referanslar B tipi / sınıf referanslarından atanabilirse, bir özyinelemeli tanımdır. Bazıları için yararlı olabilir, bazıları için kafa karıştırıcı olabilir. Birisinin faydalı bulması ihtimaline karşı ekliyorum. Bu sadece anlayışımı yakalama çabasıdır, resmi tanım değildir. Belirli bir Java VM uygulamasında kullanılır ve birçok örnek program için çalışır, bu yüzden isAssignableFrom'un tüm yönlerini yakaladığından emin olamamam da tamamen kapalı değildir.
"2" performansı açısından konuşmak (JMH ile):
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();
}
}
O verir:
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
Böylece şu sonuca varabiliriz: instanceof isInstance () ve isAssignableFrom () kadar hızlı değil (+% 0,9 yürütme süresi). Yani ne seçerseniz seçin gerçek bir fark yok
Eylemde göstermek için bazı örneklere ne dersiniz ...
@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));
}
ekibimizde yaptığımız bazı testler bunun A.class.isAssignableFrom(B.getClass())
daha hızlı çalıştığını gösteriyor B instanceof A
. çok sayıda öğe üzerinde kontrol etmeniz gerekirse bu çok yararlı olabilir.
instanceof
varsa, ciddi tasarım sorunlarınız olduğuna inanıyorum ...