• Post Reply Bookmark Topic Watch Topic
  • New Topic

Example from Angelica Langer FAQ - need help  RSS feed

 
Adrian Sosialuk
Ranch Hand
Posts: 57
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi everyone,

I found this example in the FAQ:



And explanation for this:

"The argument of type U is fine because the declared argument type is an unknown
supertype of U . But the argument of type AcceptingTask<U> is a problem. The declared
argument type is an instantiation of Task for an unknown supertype of U . The compiler
does not know which supertype and therefor rejects all argument types. "

I have to admit that I am totally lost here. For me, calling accept method which takes
AcceptingTask<U> and U is fine :/ AcceptingTask is a Tak and U is a super type of
? super U. Could someone with a lot of patience to unteachable people could
explain it to me please ?


Thanks,

Adrian
 
Sergio Tridente
Ranch Hand
Posts: 329
Java Linux Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK. I think I understand where the conflict is:

Call to accept() inside go() method of class AcceptingTask<U> is expecting arguments of type ? super U as in: accept(Task<? super U>, ? super U)

Because... acceptor argument (in method go()) is of type ? super U... and accordingly to the definition of accept() method inside Acceptor interface, both arguments should be of the same type that the one of the class. Which in this case will be ? super U.

Now the problem is that we are using this as argument call on accept() method... and this is of type U and not ? super U.

I know I am not being very clear, but I think this is it.

[edit]

Note that in the case of the second argument to accept() method it is fine to use U where ? super U is expected because:

is normal polymorfism.

But this is not true for generic classes (remember generic classes are not covariant - an example of covariantism in java will be arrays where you can assign an Integer[] to a Number[] or to an Object[]).

Examples:
Number n = new Integer(1); //fine
ArrayList<Number> ln = new ArrayList<Integer>(); //wrong
[ June 10, 2007: Message edited by: Sergio Tridente ]
 
Adrian Sosialuk
Ranch Hand
Posts: 57
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Sergio !

Thanks for your reply

> Call to accept() inside go() method of class AcceptingTask<U> is expecting > arguments of type ? super U as in: accept(Task<? super U>, ? super U)

Yes - that's right !

[...]

> Note that in the case of the second argument to accept() method it is fine > to use U where ? super U is expected because:
>
> code:
>
> ? super U var = new U();
>
> is normal polymorfism.

Yeah - correct

> But this is not true for generic classes (remember generic classes are not
> covariant - an example of covariantism in java will be arrays where you
> can assign an Integer[] to a Number[] or to an Object[]).
>
> Examples:
> Number n = new Integer(1); //fine
> ArrayList<Number> ln = new ArrayList<Integer>(); //wrong

Yes - I also understand it. However, if you have this signature:

then you can pass call it for example:

Can't I ?

Moreover, ? super T means then I can pass T as well. So in my previous
example, I can't see any violation there ...

Call acceptor.accept(this, result) takes, as you said,
AcceptingTask<U> and U, where U can be assign to ? super U
and AcceptingTask<U> should accepted by Task<? super U> ...

It's still not very clear to me :/

Just to clarify, when you change signature of the method accept
in Acceptor interface to:

then everything works fine.

If you have any idea how you could explain it, it would
be of much appreciation

Cheers,

Adrian
 
Adrian Sosialuk
Ranch Hand
Posts: 57
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi,

OK - I think I understand it now.

where a takes Task<Number> and Number while we pass Task<Integer> and
Integer. While Number=Integer is fine, we cannot assign Task<Integer> to
Task<Number>. Is it that reason why it is not allowed ?

Also, Angelica Langer says: "The argument of type U is fine because the
declared argument type is an unknown supertype of U . But the argument of
type AcceptingTask<U> is a problem. The declared argument type is an
instantiation of Task for an unknown supertype of U. The compiler does
not know which supertype
and therefor rejects all argument types."

What does she mean by saying "which supertype" ? Is it that compiler
can't be sure whether the type parameter for AcceptingTask is THE SAME
as Acceptor's ? If so, what she says here is misleading because as
long as there are two different types, it is not going to work, regardless
of Acceptor's type argument in the inheritance tree (in other words,
if we instantiate AcceptingTask<Integer>, then whether we pass
Acceptor<Number> or Acceptor<Object> it doesn't matter at all - it won't
work). So if I was her, I would put it that way: "The compiler does not
know whether AcceptingTask's type parameter is the same as Acceptor's
tyep parameter ..." Am I right here ?

Cheers,

Adrian
 
Sergio Tridente
Ranch Hand
Posts: 329
Java Linux Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Adrian,

I am now understanding what your doubts are (in fact I am not sure any more about my previous answer: it does not look correct).

As you said, this is correct:
Originally posted by Adrian Sosialuk:

then you can pass call it for example:


In fact (the same outside an static context):

It is correct too.

However, the example in Angelica's Lange FAQ is still giving trouble.
I tried modifiying the code a bit to this:


As you can see this version of the example uses Integer and Number as types instead of U and ? super U.
The compiler gives the following error:

This message looks very similar to the one given for the original version of the code.

The problem looks more evident now:
- our ConcreteAcceptor is of type Number
- accept() method in ConcreteAcceptor class expects (for a ConcreteAcceptor<Number> ) arguments of type Task<Number> and Number.
- assigning a ConcreteAcceptor<Number> to a ConcreteAcceptor<? super Integer> is syntactically correct
- Now let's take a look at line: ca.accept(at, new Integer(1));
It seems right because: ca is a ConcreteAcceptor<? super Integer> and as such it would expect a Task<? super Integer> as first argument. We are passing a Task<Integer> which can be assigned to a Task<? super Integer> but it fails. Why?
Well, it really looks ugly when you change:

by

because in ca.accept(at, new Integer(1)) we are passing a Task<Integer> where a Task<Number> is expected. And that would be wrong.

Fortunately, the compiler does nto allow to do that from the very beginning.


I cannot give you a real explanation of how this works, but after analyzing the implications in the modified version of the code, it looks good that the compiler is able to reject those kinds of conversions.
[ June 11, 2007: Message edited by: Sergio Tridente ]
 
Sergio Tridente
Ranch Hand
Posts: 329
Java Linux Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
First of all, I apologize for my previous post as I posted without noticing your previous answer.

Originally posted by Adrian Sosialuk:
where a takes Task<Number> and Number while we pass Task<Integer> and
Integer. While Number=Integer is fine, we cannot assign Task<Integer> to
Task<Number>. Is it that reason why it is not allowed ?


I think you got it right here.

Originally posted by Adrian Sosialuk:
What does she mean by saying "which supertype" ? Is it that compiler
can't be sure whether the type parameter for AcceptingTask is THE SAME
as Acceptor's ?


That is exactly the problem we don't know which supertype. Take a look again at my previous' post example:


When we say ConcreteAcceptor<? super Integer> we don't know if it is ConcreteAcceptor<Integer>, ConcreteAcceptor<Number> or ConcreteAcceptor<Object>. But if we are passing AcceptingTask<Integer> chances are 2/3 that it might go wrong.
 
Adrian Sosialuk
Ranch Hand
Posts: 57
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
> First of all, I apologize for my previous post as I posted without
> noticing your previous answer.

No problem - it happens to me all the time

>> where a takes Task<Number> and Number while we pass Task<Integer> and
>> Integer. While Number=Integer is fine, we cannot assign Task<Integer> to
>> Task<Number>. Is it that reason why it is not allowed ?

> I think you got it right here.

Hooraaaayyyy That's a release - I've been thinking about it
for the last few days. It should be included in one of the books
aka Java Puzzles It's very misleading - although it looks like
we pass SomeClass<T> to SomeClass<? super T> it is not working (and
rightly because of the famous erasure ...). I guess compiler somehow
recognizes explicit passes to bounded wildcards from implicit ones -
like in the example.


>> What does she mean by saying "which supertype" ? Is it that compiler
>> can't be sure whether the type parameter for AcceptingTask is THE SAME
>> as Acceptor's ?


> That is exactly the problem we don't know which supertype.
[...]

I agree, but still I thing she wasn't very precise here. If I had read
"because compiler doesn't know whether supertype is THE SAME as
AcceptingTask's type" it would make this whole thing easier to understand.

Anyway, thanks a lot for helping me in understanding this
rather complex example !

Cheers,

Adrian
 
Don't get me started about those stupid light bulbs.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!