Java编译器和Eclipse IDE编译器之间有一个与泛型相关的有趣差异

4
我在javac和Eclipse IDE编译器之间发现了一个有趣的差异,而且无法确定谁是正确的。所以,下面的代码在javac中编译,但是Eclipse告诉我静态初始化程序对"exportAll"的调用是错误的,因为:

类型X中的方法exportAll(Iterable< X.Stat< ? extends Number>>)对于参数(Collection< capture#1-of ? extends X.Stat>)不适用

谁是正确的?是javac还是Eclipse?
import java.util.Map;

public class X {
  interface Stat<T> {
  }

  public static void exportAll(Iterable<Stat<? extends Number>> vars) {
  }

  public static Map<Double, ? extends Stat> getPercentiles() {
    return null;
  }

static {
  exportAll(getPercentiles().values());
}

}


1
getPercentiles也应该是静态的,不是吗?否则它在任何一种情况下都无法编译 ;) - emboss
尽管有这个例子,但是在Eclipse Java编译器中仍然存在错误,它会直接拒绝Sun Java编译器所接受的泛型构造。最近我遇到了相反的情况,即Eclipse 3.7接受了一个推断,而Sun的6u21却拒绝了。 - Ron
3个回答

1

你的 Map<Double, ? extends Stat>.values() 的类型将会是 Collection<? extends Stat>。这里的 Stat 实际上是 Stat<?>。你的 Iterable 要求 Stat 不仅仅是任意的 Stat<?>,而是 Stat<? extends Number>。你需要将你的 getPercentiles() 改为 Map<Double, ? extends Stat<? extends Number>>

public static void exportAll(Iterable<Stat<? extends Number>> vars) {
}                                  //      ^ this must match
                                   //     your values type on the map

public Map<Double, ? extends Stat<? extends Number>> getPercentiles() {
    return null;
}

@emboss 在这种情况下,我会尽可能地这样做:

class Main {

    static interface Something<E> {
        void doSomething();
    }

    static class ConcreteSomething<E> implements Something<E> {
        E data;
        ConcreteSomething(E data) {
            this.data = data;
        }
        public void doSomething() {
            System.out.println(data);
        }
    }


    public static void main(String[] args) {
        List<Something<Number>> list = new LinkedList<Something<Number>>();
        list.add(new ConcreteSomething<Number>(Math.PI)); // an autoboxed Double
        list.add(new ConcreteSomething<Number>(new Integer(5))); // an Integer

        for(Something<Number> s : list) s.doSomething();
    }
}

有趣的是,在Eclipse中也不起作用;我尝试了几种变化。然而,这个可以(移除“? extends”):public static Map> getPercentiles() { return null; } - Attila Szegedi
个人而言,我不喜欢使用? extends符号。我会使用Stat<Number>,如果有人想要放入Stat<Double>Stat<BigInteger>,我也不会介意他们这样做。 - corsiKa
2
@glowcoder:那样行不通,Stat<Double>不是Stat<Number>的子类型。必须使用丑陋的“?extends”... - emboss
@emboss,你的看法似乎是正确的。我会修改一下,表达出我更愿意使用支持泛型的类。举个例子,我会编辑一些代码来说明如何使用泛型和多态(在进行一些修订后)。当然,如果我正在使用的库不支持它,那么我也无能为力! - corsiKa

1

你无法编译你的示例 - 你正在从静态初始化器中调用一个非静态方法getPercentiles,所以我会假设它也是一个静态方法。

无论如何,如果你使用-XLint:unchecked编译(Stat需要一个类型参数!),你的编译器至少会输出一个“unchecked”警告。我假设你想要以下内容:

public class X {
    interface Stat<T> {
}

public static void exportAll(Iterable<? extends Stat<? extends Number>> vars) {
}

public static Map<Double, ? extends Stat<Double>> getPercentiles() {
    return null;
}

static {
    exportAll(getPercentiles().values());
}

我假设你的百分位数是Stat<Double>的任意子类,因此我在Map中将它们声明为? extends Stat<Double>。所以values()调用将返回一个Collection<? extends Stat<Double>>

Collection实现了Iterable,因此我们在这方面是安全的。但是Collection<? extends Stat<Double>>不包含在Iterable<Stat<? extends Number>>中,因此我们需要将参数声明为Iterable<? extends Stat<? extends Number>>

拥有exportAll接受Iterable<? extends Stat<? extends Number>>的优点(好吧,除了语法之外)是你的Map可以包含各种? extends Stats<N>,其中NNumber的子类。


谢谢。现在,这并不是我编写的代码,我只是试图使用它——我的用法是“exportAll(getPercentiles().values())”这一行。我可以找到一种修改它的方法;事实上,您提出的解决方案是相同的。然而,我的问题是为什么它可以通过javac编译,但不能通过Eclipse编译。正如@irreputable正确地指出的那样,这个小例子也无法通过javac编译,这非常令人困惑,因为它可以编译我从中提取了这个例子的大型源代码体。 - Attila Szegedi

-1

在 javac 中它无法编译。(在 getPercentiles 上添加 static 后)

搞清事实,不要浪费别人的时间。

如今的孩子,多动症太多了。


嗯...看来你是对的;我试图从一个庞大的构建中提取出一个小的可重现案例,那个构建肯定编译通过(这很令人困惑)。我还验证了小的可重现案例在Eclipse中无法编译,但你说得对,我没有通过javac运行小案例。它确实会出现编译错误,让我想知道为什么大型构建会成功。我现在必须深入挖掘一下。 - Attila Szegedi

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