0

I'm beginner at Generic in Java and I'm following a course about this concept. the instructor of this course explain about these code this way:

Codes:

public class Instructor extends User { // --- }

--

public class Utils {
    public static void printUsers(GenericList<User> users){
        // ---
    }
}

--

public class Test {
    public static void main(String[] args) {

         var users = new GenericList<Instructor>();

         Utils.printUsers(instructors);
    }
}

his explanations:

in the main method , we get compilation error because a GenericList of Instructor is not a subtype of GenericList of User. I mean GenericList<Instructor> does not extend and inherit GenericList<User>.

why is that? we looked at bytecode representation of this GenericList class. as I'd show you earlier this GenericList internally maintains a list of Object s no matter what we pass as a type parameter.

whether we passed the User Class or Instructor Class, we only have a single GenericList in our project and this class is not a subtype of itself. that is the reason why a GenericList of Instructor is not a subtype of GenericList of User. because we're dealing with a single class.

Now; I'm totally confused. very very confused!

  1. Why does he say we have only a single class? GenericList<Instructor> and GenericList<User> are two class, although they have same byte code. (because both of them maintain list of Object and compiler will replace every T with Object)

  2. If we only have a single GenericList in our project and this class is not a subtype of itself, then there is no different between instance of GenericList<Instructor> and instance of GenericList<User> and they are both belong to the same class.

Why did I get such a result? because according to the teacher, we only just have a single class then why printUsers method does not accept an instance of GenericList as an argument?

and at the end , I don't understand anything about this paragraph at all :

whether we passed the User Class or Instructor Class, we only have a single GenericList in our project and this class is not a subtype of itself. that is the reason why a GenericList of Instructor is not a subtype of GenericList of User. because we're dealing with a single class.

can anyone help me to understand these explanation? Thank You.

  • 1
    The type `GenericList` is generic in a `T` type and not in a set of types. For example, if you defined `GenericList extends T>` then that code would work. – chriptus13 Jun 18 '20 at 00:15
  • 1
    Check [this](https://dzone.com/articles/how-do-generic-subtypes-work). – chriptus13 Jun 18 '20 at 00:16
  • 2
    Generic classes are the same class; you don't have two classes. You have two references to a generic classes with their associated compile-time type info. And, as you noted, that type information is not present at runtime (type erasure). https://docs.oracle.com/javase/tutorial/java/generics/erasure.html – Dave Newton Jun 18 '20 at 00:16
  • @DaveNewton in this link, he assumes as two clases : https://dzone.com/articles/how-do-generic-subtypes-work. is he wrong? – Mohammad Mehdi Sarfejoo Jun 18 '20 at 00:20
  • and if I have two reference, then these two reference should belongs to 'GenericList'. Then why 'printUsers' method does not accept 'users' reference as an argument? – Mohammad Mehdi Sarfejoo Jun 18 '20 at 00:26
  • 1
    @MehSar I'd say he's *imprecise*, and there's a distinction between compile time and run time. It's the same class (which is the point of generics) with different types (at compile time). – Dave Newton Jun 18 '20 at 00:35

1 Answers1

1

It is correct that the class GenericList is a single class. Generics are a compile-type safety, but they are erased by the compiler, so they're not available at runtime1.

However, the explanation of the instructor stating that this is the reason that GenericList<User> is not a GenericList<Instructor>, is at least questionable2.

A Dog is an Animal, but a List<Dog> is not a List<Animal>. Generics are invariant. However, there would technically be no problem if they were not. They could have provided the compiler with a type system which allowed the usage of any subtype of the generic type T in place of T itself. Type erasure could then still be applied.

This, on the other hand, was a design decision. Jon Skeet points out an important reason to why generics are invariant.


In order to make it work, modify the method signature of printUsers:

public static <T extends User> void printUsers(GenericList<T> users) {

Or, since you're not really caring of which particular subtype of User the GenericList contains, just

public static void printUsers(GenericList<? extends User> users) {

1 One of the reasons that led to the desicion that generics are erased, is that if they were not, that would mean they would be required to modify the byte code structure of a class file, breaking backward-compatibility with previous Java versions.

2 Or stronger, I would dare to call it inaccurate.
GenericList is one class (and not two) because of type erasure.
• A GenericList<User> is not a GenericList<Instructor> because of generic invariance.
Those are two different reasons for two different concepts.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130