0

I understand that while List< Integer> is not a subtype of List< Number>, but it is subtype of List< ? extends Number>.

But as Java uses concept of type erasure to implement generics, meaning that we ultimately have single class for all representations, and hence we don't have any class hierarchy defined for such classes in bytecode, then how does Java establishes this hierarchy?

Nikos M.
  • 8,033
  • 4
  • 36
  • 43
Mandroid
  • 6,200
  • 12
  • 64
  • 134
  • what do you mean *"we don't have any class hierarchy defined for such classes in bytecode"*? Java keeps meta information internally – Nikos M. May 24 '20 at 07:59
  • possible duplicate https://stackoverflow.com/questions/45802927/java-generics-is-any-meta-information-about-the-generic-type-preserved-at-runt – Nikos M. May 24 '20 at 08:00
  • Ok, and what is that meta information exactly? – Mandroid May 24 '20 at 08:00
  • 2
    Do you realise that Java _can be_ more powerful than byte code, even though one compiles to the other? The Java compiler can have its own rules of what's valid and what's not, its own hierarchies of types, and so on, in addition to the JVM's (bytecode) rules. – Sweeper May 24 '20 at 08:00
  • another possible duplicate https://stackoverflow.com/questions/29375575/java-generics-type-erasure-byte-code – Nikos M. May 24 '20 at 08:10

1 Answers1

3

Java is not bytecode, it compiles to bytecode. It is precisely because of this that it can be made a more powerful language than bytecode.

The Java Language is specified in the Java Language Specifications. There will be lots of things in this spec that the JVM doesn't know about. Parameterised types being one of them. But that's fine, because it is the job of the compiler to translate Java source code to something that the JVM can understand. In the case of generic types, this means erasing the generic parameters, and adding lots of casts.

hence we don't have any class hierarchy defined for such classes in bytecode, then how does Java establishes this hierarchy?

To the Java compiler, List<? extends Object> and List<Object> are different types. This is necessary to enforce type-safety. Does it matter that the JVM thinks they are the same? Not really, because the compiler is a standalone program itself. It can stop your code from compiling/allow your code to compile without "consulting" the JVM. The compiler knows that List< Number> is a subtype of List< ? extends Number>. It knows the type hierarchy, so it can enforce it at compile time.

What about runtime then? As you'd imagine, this type hierarchy of generics cannot be enforced at runtime, because the JVM doesn't know about generics. e.g. this doesn't throw an exception:

public class Main {

    public static ArrayList<Object> list;

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ArrayList<String> list = new ArrayList<>();
        // here we are doing the equivalent of:
        // Main.list = list;
        // using reflection (hence at runtime)
        Main.class.getField("list").set(null, list);
    }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313