RFR: 8280035: Use Class.isInstance instead of Class.isAssignableFrom where applicable
Andrey Turbanov
aturbanov at openjdk.java.net
Mon Feb 21 12:12:55 UTC 2022
On Thu, 13 Jan 2022 08:25:22 GMT, Andrey Turbanov <aturbanov at openjdk.org> wrote:
> Method `Class.isAssignableFrom` is often used in form of:
>
> if (clazz.isAssignableFrom(obj.getClass())) {
> Such condition could be simplified to more shorter and performarnt code
>
> if (clazz.isInstance(obj)) {
>
> Replacement is equivalent if it's known that `obj != null`.
> In JDK codebase there are many such places.
>
> I tried to measure performance via JMH
>
> Class<?> integerClass = Integer.class;
> Class<?> numberClass = Number.class;
>
> Object integerObject = 45666;
> Object doubleObject = 8777d;
>
> @Benchmark
> public boolean integerInteger_isInstance() {
> return integerClass.isInstance(integerObject);
> }
>
> @Benchmark
> public boolean integerInteger_isAssignableFrom() {
> return integerClass.isAssignableFrom(integerObject.getClass());
> }
>
> @Benchmark
> public boolean integerDouble_isInstance() {
> return integerClass.isInstance(doubleObject);
> }
>
> @Benchmark
> public boolean integerDouble_isAssignableFrom() {
> return integerClass.isAssignableFrom(doubleObject.getClass());
> }
>
> @Benchmark
> public boolean numberDouble_isInstance() {
> return numberClass.isInstance(doubleObject);
> }
>
> @Benchmark
> public boolean numberDouble_isAssignableFrom() {
> return numberClass.isAssignableFrom(doubleObject.getClass());
> }
>
> @Benchmark
> public boolean numberInteger_isInstance() {
> return numberClass.isInstance(integerObject);
> }
>
> @Benchmark
> public boolean numberInteger_isAssignableFrom() {
> return numberClass.isAssignableFrom(integerObject.getClass());
> }
>
> @Benchmark
> public void numberIntegerDouble_isInstance(Blackhole bh) {
> bh.consume(numberClass.isInstance(integerObject));
> bh.consume(numberClass.isInstance(doubleObject));
> }
>
> @Benchmark
> public void integerIntegerDouble_isAssignableFrom(Blackhole bh) {
> bh.consume(integerClass.isAssignableFrom(integerObject.getClass()));
> bh.consume(integerClass.isAssignableFrom(doubleObject.getClass()));
> }
>
> `isInstance` is a bit faster than `isAssignableFrom`
>
> Benchmark Mode Cnt Score Error Units
> integerDouble_isAssignableFrom avgt 5 1,173 ± 0,026 ns/op
> integerDouble_isInstance avgt 5 0,939 ± 0,038 ns/op
> integerIntegerDouble_isAssignableFrom avgt 5 2,106 ± 0,068 ns/op
> numberIntegerDouble_isInstance avgt 5 1,516 ± 0,046 ns/op
> integerInteger_isAssignableFrom avgt 5 1,175 ± 0,029 ns/op
> integerInteger_isInstance avgt 5 0,886 ± 0,017 ns/op
> numberDouble_isAssignableFrom avgt 5 1,172 ± 0,007 ns/op
> numberDouble_isInstance avgt 5 0,891 ± 0,030 ns/op
> numberInteger_isAssignableFrom avgt 5 1,169 ± 0,014 ns/op
> numberInteger_isInstance avgt 5 0,887 ± 0,016 ns/op
First of all, I did check that replace is definitely valid.
public static void main(String[] args) {
List<Class<?>> classes = List.of(Integer.class, Number.class, Serializable.class,
boolean.class, byte.class, short.class, int.class, long.class, char.class, float.class, double.class,
Boolean.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Character.TYPE, Float.TYPE, Double.TYPE,
Boolean.class, Byte.class, Short.class, Integer.class, Long.class, Character.class, Float.class, Double.class);
List<Object> objects = List.of(
new Integer(4), new BigInteger("345345"), new StringBuilder("df"),
true, false, (byte)1, (byte)0, (short)1, (short)0, (int)1, (int)0, 1L, 0L, (char)1, (char)0, 1f, 0f, 1d, 0d,
new Boolean(true), new Byte((byte)3), new Short((short)4), new Integer(5), new Long(6), new Character((char)7), new Float(8f), new Double(9d),
new ClassInstanceAssignableCheck());
for (Class<?> clazz : classes) {
System.out.println("Check " + clazz);
for (Object object : objects) {
checkIsSave(object, clazz);
}
System.out.println();
}
}
private static void checkIsSave(@Nonnull Object object, @Nonnull Class<?> clazz) {
boolean isInstance = clazz.isInstance(object);
boolean assignableFrom = clazz.isAssignableFrom(object.getClass());
System.out.println(" " + object + " of class " + object.getClass().getSimpleName() + " check with " + clazz.getSimpleName() + ": isInstance=" + isInstance + " isAssignableFrom=" + assignableFrom);
if (isInstance != assignableFrom) {
throw new RuntimeException("Not matched for " + object + " and " + clazz);
}
}
>What client tests have you run that touch the code you are changing ?
I did run tier4. It includes all client tests as I know. Some of them failed, when I run in batch. But I rechecked all failed tests one-by-one and they are fine.
Can you suggest which test better to run?
-------------
PR: https://git.openjdk.java.net/jdk/pull/7061
More information about the hotspot-jfr-dev
mailing list