Example from Angelica Langer FAQ - need help

"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 ?

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[]).

> 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

posted 10 years ago

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 ?

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 ]

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.

> 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 !