【问题标题】:What am I doing wrong with Java generics in this interface and method?我在这个接口和方法中对 Java 泛型做错了什么?
【发布时间】:2011-01-04 21:36:12
【问题描述】:

编辑:我错过了这里的另一个皱纹,结果证明有很大的不同。 doAnotherThing 的方法签名改为:

<T extends Bar> T doAnotherThing(List<Foo<T>> foo) {
    return foo.get(0).doSomething();
}

忽略它是List 的事实,只需注意List 是通用类/接口这一事实。我这样调用方法:

doAnotherThing(new ArrayList<FooImpl>);

所以,我有一个这样定义的类和接口:

abstract class Bar {
    // some neat stuff
}

class BarImpl extends Bar {
    // some cool stuff
}

interface Foo<T extends Bar> {
    T doSomething();
}

class FooImpl implements Foo<BarImpl> {
    BarImpl doSomething() {
        // Does something awesome
    }
}

这一切都很好,花花公子,效果很好。

现在,我有这样的方法:

<T extends Bar> T doAnotherThing(List<Foo<T>> foo) {
    return foo.get(0).doSomething();
}

此方法是完全不同类中的泛型方法,不属于上述链。

但是,当我尝试以下列方式使用此方法时,我收到一条错误消息,指出类型不匹配:

doAnotherThing(new FooImpl());

FooImpl 实现了Foo&lt;T&gt;,所以我看不出这是一个错误?也许我误解了什么?谢谢

【问题讨论】:

  • 在您的示例中,BarImpl 不扩展 Bar。
  • 它对我有用。除了你忘了BarImpl extends Bar
  • 你能分享一下编译器错误信息吗?
  • 类型***中的方法doAnotherThing(Foo)不适用于参数(FooImpl)
  • 也许更好地解释文件结构和方法 doAnotherThing 的位置会有所帮助,因为我能够毫无困难地复制。 Bar.java: 抽象类 Bar{ public Bar(){ doAnotherThing(new FooImpl()); } T doAnotherThing(Foo foo){ return foo.doSomething(); } static class BarImpl extends Bar{} interface Foo{ T doSomething(); } 静态类 FooImpl 实现 Foo{ public BarImpl doSomething(){ return null; } } }

标签: java generics inheritance types


【解决方案1】:

答案已更改以反映问题的说明

编译错误(类型不匹配)实际上是完全正确的。请注意,方法定义说参数是List&lt;Foo&lt;T&gt;&gt;。这意味着列表可以包含任何Foo&lt;T&gt;,并且该方法甚至必须能够添加任何实现Foo&lt;T&gt; 的对象。当你给它一个List&lt;FooImpl&gt; 时,情况并非如此,因为它只允许包含FooImpl 的实例。这有效:

doAnotherThing(new ArrayList<Foo<BarImpl>>());

混合泛型和多态会很快导致非常复杂的场景,因此应该谨慎使用。

【讨论】:

  • 迈克尔,这应该修复。请再次查看问题。谢谢。
  • @Polaris878:改变了答案,我想我现在知道了。
【解决方案2】:

为我工作。我怀疑是什么导致了错误,它不在您上面提到的版本中。下面的代码(所有一个文件,FooBarBaz.java)是否为您编译?我必须对您的代码进行的唯一更改是公开FooImpl.doSomething()。 (哦,让它返回一些东西。:)

public class FooBarBaz {

    <T extends Bar> T doAnotherThing(Foo<T> foo) {
        return foo.doSomething();
    }

    public static void main(String[] args) {
        new FooBarBaz().doAnotherThing(new FooImpl());
    }
}

abstract class Bar {
    // some neat stuff
}

class BarImpl extends Bar {
    // some cool stuff
}

interface Foo<T extends Bar> {
    T doSomething();
}

class FooImpl implements Foo<BarImpl> {
    public BarImpl doSomething() {
        return null;
    }
}

在 Mac OS 10.6.5 上使用 IDEA 9、JDK 1.6 对我来说很好。

【讨论】:

  • 你是对的,这确实有效。但是我错过了我在上面强调的一个(主要)细节。
【解决方案3】:

假设BarImpl extends Bar,我相信你从一开始就是这个意思。

增强的Foo 界面是什么样子的?

是吗:

interface Foo<T extends Bar> {
    T doSomething();
    T doAnotherThing(Foo<T> foo);
}

?

在这种情况下,一切都适用于以下 impl:

 class FooImpl implements Foo<BarImpl> {

 public BarImpl doSomething() {
  return null;
 }

 public BarImpl doAnotherThing(Foo<BarImpl> foo) {
  return null;
 }

}

现在,如果你以其他方式定义它可能会出现问题:

interface Foo<T extends Bar> {
    T doSomething();
    <T extends Bar> T doAnotherThing(Foo<T> foo);
}

因为这种定义doAnotherThing 的方式引入了另一个泛型参数。是什么混淆了接口的参数和方法中的一个参数共享名称,即T。 (顺便说一句,我很惊讶地发现 Java 允许这种令人困惑的名称冲突)

最后一个定义可以替换为:

interface Foo<T extends Bar> {
    T doSomething();
    <Y extends Bar> Y doAnotherThing(Foo<Y> foo);
}

这更清楚地说明了为什么 public BarImpl doAnotherThing(Foo&lt;BarImpl&gt; foo) 不是覆盖此方法的正确方法。

【讨论】:

  • doAnotherThing 不是Foo&lt;T&gt; 的一部分,它是另一个类的一部分
  • 我明白了。我的答案是盲目猜测。
【解决方案4】:

我相信我原来的答案是不正确的。以下似乎工作正常。只需公开 doSomething 方法,它就可以在 Java6 下正常编译。

abstract class Bar {
    // some neat stuff
}

class BarImpl extends Bar {
    // some cool stuff
}

interface Foo<T extends Bar> {
    public T doSomething();
}

class FooImpl implements Foo<BarImpl> {
    public BarImpl doSomething() {
        return null;
    }
}


public class Test {
<T extends Bar> T doAnotherThing(Foo<T> foo) {
    return foo.doSomething();
}

}

【讨论】:

    猜你喜欢
    • 2023-03-31
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 1970-01-01
    • 2013-06-28
    • 2022-11-18
    • 2021-10-04
    • 1970-01-01
    相关资源
    最近更新 更多