java-classmate icon indicating copy to clipboard operation
java-classmate copied to clipboard

Should resolve(Long[].class) return a ResolvedType whose parent type is null?

Open ljnelson opened this issue 5 years ago • 3 comments

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:

  • null as the return value of its getParentClass() method
  • an empty List<ResolvedType> as the return value of its getImplementedInterfaces() method

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

ljnelson avatar Feb 24 '20 22:02 ljnelson

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?

cowtowncoder avatar Feb 24 '20 22:02 cowtowncoder

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 avatar Feb 24 '20 22:02 ljnelson

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;

ljnelson avatar Feb 24 '20 23:02 ljnelson

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 avatar Oct 12 '23 01:10 cowtowncoder

Added simple reproduction, hoping to tackle this soon.

cowtowncoder avatar Nov 16 '23 05:11 cowtowncoder