I just read the chapter on Generics of the "K&B Book" but the (good) explanations given by the authors are not enough for me to feel comfortable with Generics. So I have questions that I wan't to ask the Java Community here:
Question 1: When we do something like:
the last line generates an error at compile-time and I understand that IS actually what we want ..but... What in the Java 1.5 List API(http://java.sun.com/j2se/1.5.0/docs/api/java/util/List.html and in the signature of the List.add(..) method) tells me that uc.add(..) on an List<?> is not permitted?
Question 2: On an List<?> is a method call like forbidden (since it doesn't kill the declared type-safety of sc) ?
Question 3: If I create my own "class MyClass<T>" with method1 an method2 like below. What would determinate if an "MyClass<?>" could call a specific method of "MyClass<T>" ?
By writing this question, I think the trick is that the argument on Line 4 should be of the same type T as the type declared at construction (i.e. String) because the same T is used on "Line 1" and "Line 2", and the problem is that the type of uc is unknown? Am I right ?
Question 4: removed
Question 5: A method call of "E (List).get(int index)" done of a reference of type "List<?>" does it return an Object? And the same get method call on "List<? extends D>" does it return an object of class D ?
Question 6: Given:
Does line 1, require a cast ? At line 2, is "???" Object? Does line 2 compile ?
Thanks in advance for your help...
NB: Sorry, some answers are obvious using javac and java, but I can't use them today.
[ January 24, 2008: Message edited by: jlinho ]
[ January 24, 2008: Message edited by: jlinho ] [ January 24, 2008: Message edited by: jlinho ]
(This code is exactly the same as the one of Java 1.4 which is:
but in addition we say to the compiler that it should check that if we create(with the constructor) a List with <E=Integer> integer into the brackets, then "add" should be used with the same E (E = Integer)! That's the additional constraint here. So for the compiler if we write:
So when the compiler tries to compile line 2, the "li" reference is of type List<Integer>. So, he can check the constraint that the E created for instanciation(Integer), is the same(or not) as the E used for "add". Now if we do:
When the compiler reads line 2, the "li" reference is of type List<?>. It means that the E used for instantiation is unknown to the compiler. At line 2, the compiler just can't check the constraint "that we are using the same E".
The remove method does not depend of the "E" used at creation (there is no "E" in the signature). So, if we have:
At line 2, the compiler sees that li is of type List<?> and has lost the info about the "E" used for instanciation... but the signature of "remove" (which does not depend on E) indicates there is no type constraint to ckeck.
Answer to Q2: Yes, a call like uc.remove(0) is allowed when uc is of type List<?> and the signature of remove indicates it is allowed.
Answer to Q3:
method1 tells the compiler to check the constraint : "The T used at creation(=instanciation) is the same used as the type of the argument of method1". So, on "Line 4", the compiler knows that uc is of type MyClass<?> but has lost the "original T of creation": the "original T" is unknown, so the compiler can't check the type of the argument used on "Line 4", so there is a compiler error on "Line 4". On line 5, the compiler does not know the "original T", but the signature "T method2()", tells the compiler there is no argument to check. But because the original T is unknown to the compiler, the return type is Object.
If the compiler had known the original T is "String", the return type would be T=String (the compiler adds a cast).
If the compiler knows only T=<? extends Animal?> the return type is an T=Animal, for T=<?>=<? extends Object> the return type is also the upperbound of ?, which is Object)
Answers to Q5 and Q6: I just explaned in the lines above that,: if the reference type of the List is known, the known type is returned.(i.e. List<String> ls = new ArrayList<String>; String s = ls.get(0) ; ) If the reference type of the List is <?>, the return type is Object(i.e. List<?> ls = new ArrayList<String>; Object o = ls.get(0) ; ) If the reference type of the list is <? extends D> the return type is T=D (i.e. List<? extends D> ls = new ArrayList<String>; D d = ls.get(0) ; ) If the reference type of the List is <? ? super E> the return type is Object (i.e. List<? super D> ls = new ArrayList<String>; Object o = ls.get(0) ; ) The compiler does a cast to the "upperbound type" of what it knows.
It helps to remember that the Java 1.5 Collections classes are the same as in Java 1.4 but the <type> informations: * just tells the compiler to do additionnal checks * the "List.get(int)" method still returns an Object, like in Java 1.4 but the <type> info tells the compiler to add a cast to the "upperbound type".
[ January 25, 2008: Message edited by: jlinho ]
[ January 25, 2008: Message edited by: jlinho ]
[ January 25, 2008: Message edited by: jlinho ] [ January 25, 2008: Message edited by: jlinho ]