以下哪个更好?
a instanceof B
或者
B.class.isAssignableFrom(a.getClass())
我知道的唯一区别是,当“a”为空时,第一个返回 false,而第二个抛出异常。除此之外,他们总是给出相同的结果吗?
使用 instanceof
时,需要在编译时知道 B
的类。使用 isAssignableFrom()
时,它可以是动态的并在运行时发生变化。
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;博士
使用具有相似性能的 isInstance 或 instanceof。 isAssignableFrom 稍微慢一些。
按性能排序:
isInstance instanceof (+ 0.5%) isAssignableFrom (+ 2.7%)
基于 JAVA 8 Windows x64 上 2000 次迭代的基准测试,其中包含 20 次预热迭代。
理论上
使用像 bytecode viewer 这样的软件,我们可以将每个运算符转换为字节码。
在以下情况下:
package foo;
public class Benchmark
{
public static final Object a = new A();
public static final Object b = new B();
...
}
爪哇:
b instanceof A;
字节码:
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A
爪哇:
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);
爪哇:
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 基准(请参阅此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();
}
}
给出了如下结果(分数是一个时间单位内的操作次数,所以分数越高越好):
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
警告
基准是 JVM 和平台相关的。由于每个操作之间没有显着差异,因此在不同的 JAVA 版本和/或 Solaris、Mac 或 Linux 等平台上可能会得到不同的结果(也可能是不同的顺序!)。
该基准比较了“B 直接扩展 A”时“B 是 A 的实例”的性能。如果类层次结构更深更复杂(例如 B 扩展 X 扩展 Y 扩展 Z 扩展 A),结果可能会有所不同。
通常建议首先选择一个运算符(最方便)编写代码,然后分析您的代码以检查是否存在性能瓶颈。也许这个运算符在您的代码上下文中可以忽略不计,或者......
关于前一点,例如,在您的代码上下文中的 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,代码在某些时候得到了优化,我们得到:
实例:6ms
isInstance:12ms
isAssignableFrom : 15ms
笔记
最初这篇文章是在原始 JAVA 中使用 for 循环进行自己的基准测试,这会产生不可靠的结果,因为像 Just In Time 这样的优化可以消除循环。因此,它主要是衡量 JIT 编译器优化循环所用的时间:有关详细信息,请参阅 Performance test independent of the number of iterations
相关问题
instanceof 运算符会产生很多开销吗?为什么?
JAVA内部是如何实现instanceof的?
在 Java 中使用 instanceof 对性能的影响
instanceof
是一个字节码,它使用与 checkcast
基本相同的逻辑(转换背后的字节码)。无论 JITC 优化程度如何,它本质上都会比其他选项更快。
isAssignableFrom()
是动态的。
a instanceof B
的更直接等价物是
B.class.isInstance(a)
这在 a
也是 null
时有效(返回 false)。
除了上面提到的基本区别外,instanceof 运算符和 Class 中的 isAssignableFrom 方法之间还有一个核心的细微差别。
将 instanceof
读作“这是(左部分)this 的实例或 this 的任何子类(右部分)”,并将 x.getClass().isAssignableFrom(Y.class)
读作“我可以写 X x = new Y()
”。换句话说,instanceof 运算符检查左对象是否相同或右类的子类,而 isAssignableFrom
则检查我们是否可以将参数类 (from) 的对象分配给调用方法的类的引用。< br> 请注意,这两个都考虑实际实例而不是引用类型。
考虑 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 所注意到的)。你的例子是正确的,但无关紧要。
Y
是抽象的或没有公共默认构造函数,则 new Y()
可能不合法,因此您可以说 X x = (Y)null
当且仅当 x.getClass().isAssignableFrom(Y.class)
为真时是合法的。
还有一个区别:
null instanceof X 是 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
与 isAssignableFrom
的不同之处,所以我想分享一些我自己的东西。
我发现使用 isAssignableFrom
是唯一(可能不是唯一,但可能是最简单的)方法来询问一个人的一个类的引用是否可以采用另一个类的实例,当一个人没有两个类的实例时比较。
因此,当我只有类时,我发现使用 instanceof
运算符比较可分配性不是一个好主意,除非我打算从其中一个类创建一个实例;我以为这会很草率。
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 的类型可能是私有内部类),则此选项无效。但是,instanceof 解决方案将始终有效。
obj
)的非空引用,那么您可以在其上调用公共 getClass()
方法以获取反射元数据以用于实现班级。即使该实现类类型在编译时在该位置不合法可见,也是如此。在运行时没问题,因为为了让您持有 obj
引用,一些最终确实 对类具有合法访问权限的代码路径创建了一个并将其提供给(泄露?)给您。
obj instanceof A
的类似物。那将是A.class.isInstance(obj)
。只有当您有两个类和零个对象时,您才应该使用 isAssignableFrom
。
一些例子来展示它的实际效果如何......
@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));
}
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 实现并适用于许多示例程序,因此虽然我不能保证它捕获了 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% 的执行时间)。所以无论你选择什么都没有真正的区别
我们在团队中进行的一些测试表明,A.class.isAssignableFrom(B.getClass())
的运行速度比 B instanceof A
快。如果您需要对大量元素进行检查,这将非常有用。
instanceof
有瓶颈,我相信你有严重的设计问题......
不定期副业成功案例分享
a instanceof Bref.getClass()
。这怎么可能是几乎没有解释(或缺乏解释)的公认答案?a instanceof Bref
而不是a instanceof Bref.class
。 instanceof 运算符的第二个参数是类名,而不是解析为类对象实例的表达式。a
并且在编译类型时知道类型B
,请使用a instanceof B
。如果您有a
,但您不知道类型B
,但您有一个对象b
,请使用b.getClass().isInstance(a)
。如果您有a
而您没有对象而是Class<?> someBClass
,请使用someBClass.isInstance(a)
。如果您有两个Class<?>
但没有实际实例化类型的对象,则一个条件是您使用someBClass.isAssignableFrom(someAClass)
时。