I cannot understand at all the following example from OCP page 124.
We have that :
and the example is as follows:
The book mentions that
method3() does not compile. <B extends A> says that you want to use B as a type
parameter just for this method and that it needs to extend the A class. Coincidentally, B
is also the name of a class. It isn’t a coincidence. It’s an evil trick. Within the scope of the
method, B can represent classes A, B, or C, because all extend the A class. Since B no longer
refers to the B class in the method, you can’t instantiate it.
I cannot understand why the Cannot instantiate the type B means. What would the correct way be to write this?
In this example B is not a class name. It is a generic parameter name. This <B extends A> means any type (further referenced by the name B) that is a subtype of A (or A itself).
If I had a class (I am assuming A is a class) how would you expect your new B() to work with type C?
to get this straight:
If for example I had a class B
In the following method:
This compiles. Because I have <T> a generic type, return type: B where B is a class, and input List < B > of objects B.
I return new B(); an instance of object B.
Instead in this example:
Here I get a compiler error because is now a generic type. So everywhere I see the "B" it is referred to the generic type and not the class.
return type of method : B -> refers to generic type now.
List< B > -> refers again to generic type and not the class B.
and return new B(); ->now it is trying to return a generic type and not class B. That is why we get a compiler error.
Is this correct?
In the first example, B is a concrete class. Ofcourse you can do "new B()" in that case, you're just creating an instance of class B.
In the second example, B is a type parameter of method3. You cannot do "new B()" when B is a type parameter. The reason why you cannot do this has to do with the way that generics are implemented in the Java compiler and JVM; using type erasure. This means that in Java, generics are purely a compile-time feature. The compiler uses generics for more type-safety, but in the actual byte code, the information about the generics is not present anymore. This means that the information about the generic types is not available to the JVM at runtime - the type parameter "B" doesn't exist anymore at runtime. You cannot do "new B()" where B is a type parameter, because at runtime there is no such thing as the type parameter "B". The JVM wouldn't know what type of object needs to be instantiated.
This is the sort of question that I really hate in these exams.
Contriving a confusing example which no programmer worth their salt would ever conceive of in 'real' code, and expecting you to interpret it 'correctly'
This is why we have naming conventions/standards.
For clarity you wanted to re-write it so that T represented the Generic type and B represented the class (and thus avoid the confusing name collision), I believe the result should be:
EDIT: You have to cast the new B() to type T to get it to compile.
One last question:
When using generics in method, according to Java doc (https://docs.oracle.com/javase/tutorial/java/generics/methods.html)
the syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method's return type
Do I have to declare the type parameters if I use generics on return type or inside the method signature? Or Both?
For example I tried out the following combinations:
According to these examples, I have to add <T> if I use it somewhere in the method( either in the return type or in the method signature.)
Is that correct?