Answers:
使用时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。isAssignableFrom稍微慢一些。
按效果排序:
基于在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
警告
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,代码在某些时候得到了优化,我们得到了:
注意
最初,该文章使用原始JAVA中的for循环进行自己的基准测试,结果不可靠,因为某些优化(如Just In Time)可以消除循环。因此,主要是测量JIT编译器花费多长时间来优化循环:有关更多详细信息,请参见性能测试,该测试与迭代次数无关
相关问题
instanceof
是的,是一种字节码,其使用的逻辑基本上与checkcast
(强制转换后面的字节码)逻辑相同。无论JITC优化的程度如何,它本质上都会比其他选项更快。
isAssignableFrom()
是动态的。
除了上述基本差异外,Class中的instanceof运算符和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)
为true 时才说是合法的。
还有另一个区别:
X的null实例false
无论X是什么
null.getClass()。isAssignableFrom(X)将抛出NullPointerException
null instanceof X
其中X是在编译时已知的某个类)将始终返回false
。
X.class.isAssignableFrom(null.getClass())
不是吗?但是,是的,调用getClass()
空引用将导致NPE。
getClass()
,一开始不应该使用isAssignableFrom
-该操作是针对没有对象的情况。如果你有对象引用a
,使用a instanceof SomeClass
(如果你做知道类型SomeClass
),或someObject.getClass().isInstance(a)
(如果你不知道的类型someObject
)。
还有另一个区别。如果要测试的类型(类)是动态的(例如,作为方法参数传递),那么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不能与原始类型或泛型类型一起使用。如以下代码所示:
//Define Class< T > type ...
Object e = new Object();
if(e instanceof T) {
// Do something.
}
错误是:无法对类型参数T执行instanceof检查。请改用它的擦除对象,因为在运行时将删除更多的通用类型信息。
由于类型删除导致无法编译,因此删除了运行时引用。但是,下面的代码将编译:
if( type.isAssignableFrom(e.getClass())){
// Do something.
}
考虑以下情况。假设您要检查类型A是否是obj类型的超类,可以选择
... A.class.isAssignableFrom(obj.getClass())...
要么
... obj instanceof A ...
但是isAssignableFrom解决方案要求obj的类型在此处可见。如果不是这种情况(例如,obj的类型可能是私有内部类),则退出此选项。但是,解决方案的实例将始终有效。
obj
在此示例中),则可以在其上调用public 方法以获取实现类的反射元数据。即使在编译时该实现类类型在该位置在法律上不可见,也是如此。这在运行时还可以,因为对于您来说,保留引用是一些代码路径,这些代码路径最终确实可以合法访问该类,因此创建了该类并将其提供(泄漏了)。getClass()
obj
isAssignableFrom(A, B) =
if (A == B) return true
else if (B == java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))
如果可以从类型/类B的引用中分配类型/类A的引用,则上面的伪代码是一个定义。它是一个递归定义。对于某些人来说可能是有帮助的,对于其他人来说则可能令人困惑。我添加它是为了防止有人觉得它有用。这仅是为了获取我的理解,并非官方定义。它用于某个Java VM实现中,并且可用于许多示例程序,因此尽管我无法保证它捕获了isAssignableFrom的所有方面,但并不能完全解决。
谈到性能“ 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
这样我们可以得出结论:instanceof的速度与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
,我相信您有严重的设计问题……