I understand the rationale for the second method not being legal (since the K parameter in this method is unbounded, and so A<K> is not within bounds.) The question is: why is the first method legal? My guess is that the compiler gives wildcards more freedom when satisfying bounds, and as long as there is some value of K for which A<? super K> satisfies the bound where the type of A must extend Number (given by the generic class declaration) the compiler will allow it. Could someone clarify?
Thanks,
Ruben
All code in my posts, unless a source is explicitly mentioned, is my own.
Marco Reuel Perez
Ranch Hand
Joined: Apr 16, 2009
Posts: 45
posted
0
Second code does not work. That's because the "K" in the method declaration is hiding the "K" in the class declaration. If you have removed the additional "K" declaration in the method, i.e :
The K in the method declaration hides the K in the class declaration and they both are unrelated. Now, when you re-declare K and then use it with class A, there is no guarantee that it is going to be in the bounds of class A's K, i.e, there is no guarantee that the K in the method extends Number.
-Reuel
SCJP 6 - 91%
<><
Ruben Soto
Ranch Hand
Joined: Dec 16, 2008
Posts: 1032
posted
0
Thanks for your answer, Marco. I agree with you that in theory that method shouldn't be legal. But, as per Devaka's solution, it is (try to run it through the javac compiler.)
These methods are both legal:
Hopefully someone can explain this.
Ruben Soto
Ranch Hand
Joined: Dec 16, 2008
Posts: 1032
posted
0
For the record, I am leaning towards this explanation: When the compiler encounters a wildcard it doesn't enforce bounds the way it does when it has a simple generic type parameter. In the case of the wildcard the compiler is satisfied with checking that an instantiation of the generic type (and an assignment to the generic type reference using the wildcard) is possible which will not break the bounds established elsewhere. I just need a more authoritative explanation.
Marco Reuel Perez
Ranch Hand
Joined: Apr 16, 2009
Posts: 45
posted
0
You are right Ruben.
When I used javac it (first abstract method in your question) worked (to my surprise). I wonder what's happening too.. When I tried in eclipse it didn't allow me as expected. But javac allows.. Strange..
Now if I modify one of the abstract methods as follows, it wouldn't work..
Interesting find Ruben. I look forward for more substantiating explanation as well.. I don't know if I am missing something here.
-Reuel
Ruben Soto
Ranch Hand
Joined: Dec 16, 2008
Posts: 1032
posted
0
Thanks for checking on this, Marco. The method that you show is actually instantiating an A<K> where K is not bounded. My working hypothesis is that when you have a wildcard as opposed to a type parameter the compiler allows it as long as it could possibly work in some cases.
Interesting that it doesn't work in Eclipse. I wonder if this is just a compiler-specific thing. It is very misleading though, since common sense says that it shouldn't work, yet it does (in javac, anyway.)
Well lets go at this in a practical way. Lets take the first method
First of all, here K has nothing to do with K in the class declaration. You can even use this
Now the return type will work that's for sure as A is declared to be a sub-type of Number and so does the return type declares. As far as the parameter goes, the compiler allows A<? super D> as it knows that D will have to resolve to a sub-type of Number (or Number itself). Otherwise the calling code won't compile. Let's take an example
So basically I fell that the compiler here knows that the wildcards won't hurt the code as the calling code has gotta be legal. And if the calling code would be legal, then the declaration
Won't create any problems. The same is the case with the other example
Here again, the return type says A<? super Number> but the compiler knows that the class A won't allow anything like this to be returned
So the compiler knows that the returned object would be A<Number>. Similarly, in the parameter, the compiler knows that D will have to resolve to Number or a sub-type of Number. So there is nothing to worry there too...
So basically when the compiler sees a wildcard it will leave the checking of bounds to code which actually instantiates the generic class.
To make sure I understand things, let's consider the method:
In this case, it's clear why the return A<K> makes this method illegal, since you could return A<K> from an actual implementation of this method, and the compiler can't check that K is within bounds. But even this method is illegal:
In this case, the compiler is preventing you from declaring an A<K> reference where K is out of bounds. You could argue that the compiler could actually let this code be legal, and check on the bounds when an instance of A is instantiated, but this is not the actual problem in this case. The actual problem is that now k is a reference of A which might be out of bounds, and you might be able to add to an actual A instance (which will be within bounds) passed as a parameter to the method an element of type K, which will break the type safety of the collection (since K is out of bounds.) This couldn't happen when the parameter had a wildcard, since you can't add elements through generic references with wildcards.
Let me know if you see any flaw in my reasoning, Ankit. And again, thanks for the great explanation.
Well Ruben you are right. When you use an identifier for the generic type, then it can resolve to anything which might be out of the bounds for the type. Lets take our own code with some changes
Now here T will resolve to Object. So this breaks the bounds for GenericExp<K extends Number>. But with wildcard, this cannot happen as we cannot use the syntax that we used in this method call to decide what the wildcard resolves to. So modifying this code will work
reji singh
Ranch Hand
Joined: Apr 06, 2009
Posts: 52
posted
0
Guys,
excellent stuff on Generics. Everytime i see questions on Generics with little play on them, I see things getting more complicated. Wonder when will i master this topic
But you guys are simply great with ideas.
Clay Chow
Ranch Hand
Joined: Nov 09, 2008
Posts: 35
posted
0
Ruben Soto wrote:\
In this case, the compiler is preventing you from declaring an A<K> reference where K is out of bounds. You could argue that the compiler could actually let this code be legal, and check on the bounds when an instance of A is instantiated, but this is not the actual problem in this case. The actual problem is that now k is a reference of A which might be out of bounds, and you might be able to add to an actual A instance (which will be within bounds) passed as a parameter to the method an element of type K, which will break the type safety of the collection (since K is out of bounds.) This couldn't happen when the parameter had a wildcard, since you can't add elements through generic references with wildcards.
Let me know if you see any flaw in my reasoning, Ankit. And again, thanks for the great explanation.
I think I am following. I agree with why/why not the return type generics compile/do not compile. But have a question with the parameter ones.
So you are saying:
(1) public abstract <K> A<? extends Number> useMe(A< ? super K> k);
(2) public abstract <K> A<K> useMe(A<K> k);
(3) public abstract <K> A<? super Number> useMe(A< ? extends K> k);
One question:
For (3), the parameter generic does compile because you are unable to add to the parameter collection. (2) would not compile because one would be able to add to the collection. However, (1) compiles, and you can still add to the parameter collection (because a collection reference with generic super, allows additions, sometimes though). So maybe this still enforces the class generic declaration.
Also, in this example, 'A' is not a collection, but just another Object that doesn't do much. However, if A was a collection (say it did extend ArrayList), you would still be able to add to the parameter collection in (1).
Another question:
It was said that the parameter generics in (1) and (3) are valid because the type parameter <K extends Number> would be checked when the 'A' is instantiated.
Could this not also apply to the parameter generic in (2) ? Since the A<K> would be instantiated (and therefore be valid) before it is passed into the method, it stands to reason that K must therefore follow <K extends Number> and would therefore be valid to add elements of type K into that collection.
You make some very good points. I was trying to make sense of this, and hadn't thought about the A<? super K> argument, only about the A<? extends K>. I think the compiler allows an explicit type when the type can be verified to be in bounds, but it allows a declaration in wildcard when the wildcard could resolve at least in some cases to a type which will be in bounds. The question that you put is valid though.
Anatole Dominguez
Greenhorn
Joined: May 12, 2010
Posts: 2
posted
0
When I try to compile this line
with Eclipse, does not compile
but, if I use Javac in the same file in Windows Console it does compile
Anatole, for the purpose of SCJP, always compile and run your program from the command line using the JDK javac and java commands. IDEs like Eclipse and Netbeans sometimes behave differently than the JDK compiler but the SCJP exam is based on the JDK compiler. So for the exam you should rely on the behavior of the JDK compiler...
I agree. Here's the link: http://ej-technologies/jprofiler - if it wasn't for jprofiler, we would need to
run our stuff on 16 servers instead of 3.