【问题标题】:I have a generic method that will not take the type I parameterized it to我有一个通用方法,它不会采用我将其参数化为的类型
【发布时间】:2012-10-23 04:02:03
【问题描述】:

我想在这里获取 G.myUglyList 列表的内容并将其传递给 Outer.send() 方法。我不明白为什么这会产生编译器错误。 ? extends Inner 是我参数化 Outer 的类型。那么为什么它拒绝传递给它的 Inner 呢?它需要一个“? extends Inner”,它不是类型。

我想要声明为List<Outer<? extends Inner>> 的列表,因此它可以采用Inner 的子类型。 (请参阅下面的编辑了解为什么会这样)

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

我收到此错误:

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>

编辑:我得到了很多答案,说只列出List&lt;Outer&lt;Inner&gt;&gt; 类型的列表,但这是不正确的。如果我这样做,我将能够添加 Inner 的子类型。如果我尝试添加Outer&lt;Inner2&gt;,它将失败。所以列表的类型必须是List&lt;Outer&lt;? extends Inner&gt;&gt;

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
  }

【问题讨论】:

  • 请注意,List&lt;Outer&lt;? extends Inner&gt;&gt; 应该用作可以处理 List&lt;Outer&lt;Inner&gt;&gt;List&lt;Outer&lt;ClassThatImplementsInner&gt;&gt; 的参数的类型,而不是用于属性声明。

标签: java generics compiler-errors


【解决方案1】:

更改您的代码:

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

它会编译

更新:

// 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
        }
    }
}

【讨论】:

  • 除非不允许我将 Inner 的子类型放入 Outer。请看我的编辑。
  • @marathon 编译器将通配符读取为未知类型(未知但有界),因此Outer&lt;? extends Inner&gt; 表示outer 没有已知类型(几乎就好像它没有任何类型一样) ),这就是编译器无法推断出 send 调用的类型的原因,它没有与通配符匹配的类型。要解决add 调用的问题,你可以写myUglyList.add((Outer) foiled);,它很乱而且会产生警告
  • 您在 Outer 的声明中将 Outer 的参数绑定到了 Inner,如果 Outer 在其他地方也用于其他用途,这通常可能是不希望的。如果说外层真的是java.util.Set,那就不行了。
  • @marathon 是的,你是对的,我把T extends Inner 部分放在那里只是为了强调添加它不起作用
【解决方案2】:

只要声明

List<Outer<Inner>> myUglyList;

然后您可以不受限制地添加Inner 的子类型。通过声明它

List<Outer<? extends Inner>> myUglyList;

您是说myUglyList 是“Outer&lt;some specific (but unknown) subtype of Inner&gt; 的列表”。这不是你想要的。

【讨论】:

  • 除非不允许我将 Inner 的子类型放入 Outer。请看我的编辑。
  • @marathon - 是的,这是正确的。通常,您不能在列表中混合类型并且Outer&lt;Inner2&gt; 不是Outer&lt;Inner&gt; 的子类型(即使Inner2Inner 的子类型)。但是,Outer&lt;Inner&gt; 可以接受 Inner2 参数到 send。也许您可以解决这个问题,或者详细说明为什么这对您不起作用。你想完成什么需要混合类型?
  • @marathon - 您面临的技术问题是 Java 中的泛型不是协变的。有一篇很好的文章 here 很好地描述了这个问题。
【解决方案3】:

outer 属于Outer&lt;? extends Inner&gt; 类型,即Inner 的某个未知子类型,它的send 方法采用相同子类型的对象。

例如outer 可能是Outer&lt;OtherInner&gt; 类型,然后outer.send 需要OtherInner,所以outer.send(xxx) 是错误的。

【讨论】:

    【解决方案4】:

    PECS - 生产者extends 消费者super

    因为outer 是用extends 参数化的,所以您不能将任何东西null 除外)传递给它的send 方法。

    【讨论】:

    【解决方案5】:
     for (Outer<? extends Inner> outer : myUglyList) {
         outer.send(xxx); //error
     }
    

    其中Outer&lt;? extends Inner&gt; outer,所以实际类型未知(?)。 send 有一些未知的东西扩展了Inner,而且outer 有一些东西扩展了Inner 但仍然未知。

    更新

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

    【讨论】:

    • @marathon:查看更新,我认为您的 Outer 可以发送任何扩展 Inner 的内容,您也可以将这些内容发送到列表中。
    • 但这会在 Outer 的声明中强制 Outer 与 Inner 绑定。假装不是 Set 的外部。外部可能用于非内部事物。
    猜你喜欢
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多