从类型限定中使用的泛型类型中访问类型参数

5

我发现泛型存在以下问题,考虑以下泛型接口:

public interface A<X> {
    X get();
    void doStuff(X x);
}

现在,假设有以下方法定义:
public <T extends A<?>> void foo(T t) {
    bar(t);
}

由于通配符的存在,get()方法返回类型的类型信息不足。因此,我不得不委托另一个方法将这个通配符“绑定”到一个新的类型变量上:
private <X> void bar(A<X> t) {
    X x = t.get();
    t.doStuff(x);
}

在 foo 中调用 bar() 是不允许的,编译器会输出以下错误信息:

类型 test 中的方法 bar(A) 对参数 (T) 不适用。

但是,如果我将方法 foo() 更改为

public <T extends A<?>> void foo(T t) {
    A<?> u = t; // No explicit cast required, no "unchecked" warning!
    bar(u);
}

它起作用。为什么?这是编译器错误吗?非常感谢任何对此的评论。

备注:

  • 我之所以不简单地声明方法 foo 为 void foo(A),是因为我实际上正在使用上界交集(&)。
  • 我之所以不在 foo() 中声明 X 为类型变量,是因为我实际上在类级别上遇到了这个问题,并且不想不必要地增加该类的类型参数数量。

你是否使用了 -Xlint 编译器标志?如果没有,请尝试使用该标志进行编译。 - Hunter McMillen
我同意@kan的观点。Java编译器确实可以编译这个程序。我使用的是1.6.0_27版本。Eclipse确实会给出你所提供的精确错误信息,所以也许你正在使用它? - Joseph Gordon
使用@kan的示例创建了Eclipse错误:https://bugs.eclipse.org/bugs/show_bug.cgi?id=364200。 - Joseph Gordon
无法使用javac 1.7.0编译。 - Joseph Gordon
2个回答

2
我已经检查了代码:
public class Test
{
    public interface A<X> {
        X get();
        void doStuff(X x);
    }
    public <T extends A<?>> void foo(T t) {
        bar(t);
    }

    private <X> void bar(A<X> t) {
        X x = t.get();
        t.doStuff(x);
    }
}

它正常工作。javac版本为1.6.0_22。您在哪里出了错误?或者我正在使用另一段代码?


谢谢,我会在周一在我的工作机器上检查它,我不知道那里使用的确切javac版本。我刚在家里测试了它,使用javac 1.6.0_11编译不通过... - misberner
顺便提一下,错误简单地说就是“在Test中的<X>bar(Test.A<X>)无法应用于(T)”,标记指向bar()调用。 - misberner

1
代码:

public class Test
{
    public interface A<X> {
        X get();
        void doStuff(X x);
    }
    public <T extends A<?>> void foo(T t) {
        bar(t);
    }

    private <X> void bar(A<X> t) {
        X x = t.get();
        t.doStuff(x);
    }
}

只为Java 1.6的特定版本编译。这个bug是为eclipse记录的,但被Srikanth Sankaran拒绝,并给出了以下解释:

我认为Eclipse编译器的行为是正确的,并且与JDK5和JDK7(最新版本)相匹配。看起来JDK6的行为是一个已经被修复的退化现象。
基本上,这里简要解释一下正在发生的事情:给定泛型方法bar和由bar(t)给出的调用站点,推断算法没有约束条件来推断类型变量X的类型。因此,在考虑参数和期望返回类型等之后,X仍然未解决,并且根据规范被解析为发布的下限 - 即X被推断为Object,方法变为void bar(A) t); 由于实际参数无法转换为形式参数,推断的方法必须被拒绝,使我们没有可适用的候选项。因此,该调用必须被拒绝。
调用bar((A)t)成功的原因是在这种情况下,X被推断为“capture#1-of ?”,并且使用此替换参数化的泛型方法变为void bar(A),其中参数兼容性成立。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接