3

I would like to take the contents of the G.myUglyList list here and pass it to the Outer.send() method. I do not understand why this gives a compiler error. ? extends Inner is the type I parameterize Outer with. So why is it rejecting an Inner passed to it? It wants a "? extends Inner", which isn't a type.

I want the list declared as List<Outer<? extends Inner>> so it can take subtypes of Inner. (Please see the edit below for why this is)

interface Outer<T> {
    void send(T message);
}
interface Inner {}
interface Inner2 extends Inner {}

public class G {

    List<Outer<? extends Inner>> myUglyList;

    void foo() {
        Inner xxx = null;
        for (Outer<? extends Inner> outer : myUglyList) {
            outer.send(xxx); //error
        }
    }
}

I get this error:

error: method send in interface Outer<T#2> cannot be applied to given types;
required: CAP#1
found: Inner<T#1>
reason: actual argument Inner<T#1> cannot be converted to CAP#1 by method invocation conversion
where T#1,T#2 are type-variables:
T#1 extends Object declared in class G
T#2 extends Object declared in interface Outer
where CAP#1 is a fresh type-variable:
CAP#1 extends Inner<T#1> from capture of ? extends Inner<T#1>

edit: I got a lot of answers saying just make the list of type List<Outer<Inner>>, but that is incorrect. I will not be able to add subtypes of Inner if I do that. If I try to add an Outer<Inner2>, it would fail. So list must be of type List<Outer<? extends Inner>>.

interface Inner2 extends Inner {}        
class G {
  void foo() {
    Outer<Inner2> foiled = null;
    myUglyList.add(foiled);   //this will fail if list is of type List<Outer<Inner>>
    Inner xxx = null;
    for (Outer<? extends Inner> outer : myUglyList) {
    outer.send(xxx); //error
  }
marathon
  • 7,881
  • 17
  • 74
  • 137
  • Note that the `List>` should be used as a type for a parameter that can handle `List>` or `List>`, not for attribute declarations. – Luiggi Mendoza Oct 23 '12 at 04:11

5 Answers5

3

Change your code for:

interface Outer<T> {
    void send(T message);
}
interface Inner {}
interface Inner2 extends Inner {}

public class G {

    List<Outer<Inner>> myUglyList;

    void foo() {
        Inner2 xxx = null;
        for (Outer<Inner> outer : myUglyList) {
            outer.send(xxx); //error
        }
    }
}

And it will compile

Update:

// No matter if you put 'T extends Inner' here, the 'add' won't compile
interface Outer<T extends Inner> {
    void send(T message);
}
interface Inner {}
interface Inner2 extends Inner {}

public class G {

    List<Outer<Inner>> myUglyList;

    void foo() {
        Outer<Inner2> foiled = null;

        // This way, the 'add' invocation will compile, but it 
        // breaks the generic and generates a warning.
        //
        // Casting 'foiled' to (Outer<Inner>) will also fail
        // because the compiler sees Outer<Inner2> as complete different type
        // from Outer<Inner>
        myUglyList.add((Outer) foiled);  

        Inner xxx = null;
        for (Outer<Inner> outer : myUglyList) {
            outer.send(xxx); //error
        }
    }
}
higuaro
  • 15,730
  • 4
  • 36
  • 43
  • Except that will not allow me to put in subtypes of Inner into Outer. See my edit, please. – marathon Oct 23 '12 at 04:21
  • @marathon The compiler reads the wildcard as an unknown type (unknown but bounded), so `Outer extends Inner>` says that `outer` does not have a known type (almost as if it does't have any), that's why the compiler fails deducing the type for the `send` invocation, it doesn't have a matching type for the wildcard. To solve the problem with the `add` invocation, you can write `myUglyList.add((Outer) foiled);`, it's messy and it will generate a warning though – higuaro Oct 23 '12 at 05:07
  • You've tied Outer's parameter to Inner in Outer's declaration, which, in general might not be desired, if Outer is used elsewhere for other things, too. If outer were actually say, java.util.Set, that wouldn't fly. – marathon Oct 23 '12 at 13:40
  • 1
    @marathon Yes, you're right, I put the `T extends Inner` part there just to emphasize that adding it won't work – higuaro Oct 23 '12 at 15:27
2

Just declare

List<Outer<Inner>> myUglyList;

You can then add subtypes of Inner without restriction. By declaring it

List<Outer<? extends Inner>> myUglyList;

you are saying that myUglyList is "a list of Outer<some specific (but unknown) subtype of Inner>". That's not what you want.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Except that will not allow me to put in subtypes of Inner into Outer. See my edit, please. – marathon Oct 23 '12 at 04:21
  • @marathon - Yes, that is correct. Generally, you cannot mix types in a list and `Outer` is not a subtype of `Outer` (even though `Inner2` is a subtype of `Inner`). However, an `Outer` can accept an `Inner2` argument to `send`. Perhaps you can make do with that, or describe more about why that would not work for you. What are you trying to accomplish that requires mixing types? – Ted Hopp Oct 23 '12 at 05:16
  • 2
    @marathon - The technical issue you are facing is that generics in Java are not covariant. There's a nice article [here](http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html) that describes the issue well. – Ted Hopp Oct 23 '12 at 05:22
2

outer is of type Outer<? extends Inner>, i.e. of some unknown subtype of Inner and its send method takes an object of that same subtype.

For example outer may be of type Outer<OtherInner> and then outer.send needs a OtherInner, so outer.send(xxx) would be wrong.

Miserable Variable
  • 28,432
  • 15
  • 72
  • 133
1
 for (Outer<? extends Inner> outer : myUglyList) {
     outer.send(xxx); //error
 }

In that, Outer<? extends Inner> outer, so the actual type is unknown (?). send takes something unknown that extends Inner, and also outer has something that extends Inner but still unknown.

Update

interface Outer<T extends Inner> {
    void send(T message); //this can take instance of subtype of Inner
}
interface Inner {}
interface Inner2 extends Inner {}

class G {

    List<Outer<Inner>> myUglyList; //you can add instances of subtypes of Inner

    void foo() {
        Inner xxx = null;
        for (Outer<Inner> outer : myUglyList) {
            outer.send(xxx); //error
        }
    }
}
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
  • @marathon: See the update, I think with that your Outer can send anything that extends Inner and you can those anything to the list too. – Bhesh Gurung Oct 23 '12 at 04:57
  • but that forces Outer to be tied to Inner in Outer's declaration. Pretend instead of outer that was Set. Outer might be used for non-Inner things. – marathon Oct 23 '12 at 13:44
1

PECS - Producer extends Consumer super

Because outer is parameterized with extends, you cannot pass anything (except null) into its send method.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • your answer gave me enough of a clue... Didn't know of PECS before. I also found this gem which explains PECS in more detail. http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs – marathon Nov 06 '12 at 21:40