• Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Generics question (Devaka Diagnostic Exam, Question 57)

 
Ruben Soto
Ranch Hand
Posts: 1032
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Given this code:

You can legally insert this at //1:


But not this:


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
 
Marco Reuel Perez
Ranch Hand
Posts: 45
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
 
Ruben Soto
Ranch Hand
Posts: 1032
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 1032
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 45
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 1032
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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.)
 
Ankit Garg
Sheriff
Posts: 9509
22
Android Google Web Toolkit Hibernate IntelliJ IDE Java Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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...
 
Ruben Soto
Ranch Hand
Posts: 1032
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Great explanation, Ankit!

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.
 
Ankit Garg
Sheriff
Posts: 9509
22
Android Google Web Toolkit Hibernate IntelliJ IDE Java Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 52
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 35
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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.
 
shweta jha
Greenhorn
Posts: 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
thanks ankit for help
 
Ruben Soto
Ranch Hand
Posts: 1032
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Clay,

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
Posts: 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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

Can someone explain that to me??
 
Ankit Garg
Sheriff
Posts: 9509
22
Android Google Web Toolkit Hibernate IntelliJ IDE Java Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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...
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic