FasterXML / java-classmate

Library for introspecting generic type information of types, member/static methods, fields. Especially useful for POJO/Bean introspection.
http://fasterxml.com
Apache License 2.0
258 stars 42 forks source link

`TypeResolver.resolve(Long[].class)` should not return a `ResolvedType` whose parent type is null #51

Closed ljnelson closed 8 months ago

ljnelson commented 4 years ago

The JLS indicates that a subclass array type should have the parent class array type as a supertype, although it stops short of saying that it should have the parent class array as an actual superclass.

The JLS says explicitly that every array type has Object.class as its superclass.

It also says that every array type directly implements Cloneable and Serializable.

If I do TypeResolver.resolve(Long[].class), the ResolvedType that results has:

Is this by design? What about Number[].class? Should that be its parent class?

cowtowncoder commented 4 years ago

Hmmh. I think this was a tricky area, and my immediate thinking is that all array types only really extended Object, so that Long[] does not have Number[] as its super class. I thought (but haven't tested) that this is how casting also works (i.e. does not take into account assignment rules of element type).

Practically speaking, I think that JDK methods for super class and interfaces should probably be followed, to keep compatibility with that part.

It is possible that code, as is, might be taking short-cuts it should not, which could explain the problem. At very least I think Object should be available as getParentClass().

So, maybe PR for test cases would make sense -- like using JDK accessors to verify some basic aspects?

ljnelson commented 4 years ago

Here are some fun JUnit assertions etc.:

final Class<?> c = Long[].class;
// Interestingly, no Number[].class:
assertEquals(Object.class, c.getSuperclass());
// Just being thorough:
assertEquals(Object.class, c.getGenericSuperclass());
final Class<?>[] interfaces = c.getInterfaces();
assertNotNull(interfaces);
assertEquals(2, interfaces.length);
assertEquals(Cloneable.class, interfaces[0]);
assertEquals(Serializable.class, interfaces[1]);
final Long[] longs = new Long[] { Long.valueOf(1L) };
// Note: no compilation error:
final Number[] numbers = longs;
// Because the JVM is magic (Number[].class is not in the reflective type hierarchy):
assertTrue(Number[].class.isAssignableFrom(Long[].class));
assertTrue(longs instanceof Number[]);
ljnelson commented 4 years ago

And just for completeness, here is some stuff with primitives:

// Now let's try some primitives:
c = int[].class;
assertEquals(Object.class, c.getSuperclass());
assertEquals(Object.class, c.getGenericSuperclass());
interfaces = c.getInterfaces();
assertNotNull(interfaces);
assertEquals(2, interfaces.length);
assertEquals(Cloneable.class, interfaces[0]);
assertEquals(Serializable.class, interfaces[1]);
final int[] ints = new int[] { 1 };
// Compilation error as expected:
// final long[] longs = ints;
cowtowncoder commented 11 months ago

Quick note: I think as per JLS, parent type for array types should be resolved java.lang.Object.

So if anyone has time and interest, PR for fix with tests -- or just tests at first -- would be appreciated.

cowtowncoder commented 10 months ago

Added simple reproduction, hoping to tackle this soon.