带有通配符的 Java 泛型在 Eclipse 中编译,但在 javac 中不编译

标签 java eclipse generics wildcard javac

作为 Java generics compile in Eclipse, but not in javac 的跟进,我发布了另一个片段,它在 Eclipse 中编译和运行良好,但在 javac 中引发编译错误。 (这可以防止从中提取代码片段的项目使用 Maven 构建。)

独立的片段:

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class Main {
  public static void main(String[] args) {
    Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
    List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
  }


  public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c) {
    List<T> list = new ArrayList<T>(c);
    java.util.Collections.sort(list);
    return list;
  }


  public static class Foo<T> implements Comparable<Foo<T>> {
    @Override
    public int compareTo(Foo<T> o) {
      return 0;
    }
  }
}

在 javac 中编译返回:

Main.java:11: <T>asSortedList(java.util.Collection<T>) in Main cannot be applied to (java.util.Set<Main.Foo<?>>)
    List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
                                    ^

关于 Foo<?> 的替换与 Foo<String>上面的代码片段将在 javac 中编译,这意味着问题与使用的通配符有关。由于 Eclipse 编译器应该更宽容,代码片段是否可能不是有效的 Java?

(我使用 javac 1.6.0_37 和编译器合规级别为 1.6 的 Eclipse Indigo)

(EDIT1:包含另一个在 EDIT2 中删除的示例。)

EDIT2: irreputable 提示, 比较 Foo<A>Foo<B>可能在概念上是错误的,并受到seh的答案的启发。 , 一个工作 asSortedFooList可以这样写:

public static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
    List<T> list = new ArrayList<T>(c);
    java.util.Collections.sort(list);
    return list;
}

(在上面的方法定义中用 Comparable<T> 简单替换 Foo<?>。) 因此,对于 javac 和恕我直言,在概念上比较任何 Foo<A> 似乎是安全的。和 Foo<B> .但是还是不能写出泛型方法asSortedList如果其类型参数使用通配符进行参数化,则返回泛型集合的排序列表表示形式。我试图通过替换 Foo<?> 来“欺骗”javac通过 S extends Comparable<S>asSortedFooList ,但这没有用。

EDIT3: 稍后 Rafaelle指出,自从实现Comparable<Foo<T>>以来,设计存在缺陷没有必要,并实现 Comparable<Foo<?>>提供相同的功能,通过改进设计解决最初的问题。

(最初的原因和好处是,一个 Foo<T> 可能在某些目的不关心它的具体类型,但仍然使用一个具体类型的实例 T ,它被实例化为, 用于其他目的。该实例不必用于确定其他 Foo 中的顺序,因为它可能用于 API 的其他部分。

具体示例:假设每个 Foo 都被实例化为 T 的不同类型参数. Foo<T> 的每个实例具有类型为 int 的递增 ID用于执行 compareTo -方法。我们现在可以对这些不同类型的列表进行排序 Foo并且不关心具体类型 T (用 Foo<?> 表示)并且 仍然 有一个具体类型的实例 T可供以后处理。)

最佳答案

对我来说这是另一个 javac漏洞。当您尝试发送 Collection<Foo<?>>到具有签名的方法:

public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c)

编译器注意到形式参数 T有一个上限,所以检查调用者是否遵守约束。 类型参数 是参数化类型 Foo<T> 的(通配符)实例化, 所以如果 Foo<?> 测试将通过是一个 Comparable<Foo<?>> .基于通用定义:

class Foo<T> implements Comparable<Foo<T>>

我会说这是真的,所以 Eclipse 再次是正确的 javac有一个错误。这Angelika Langer's entry永远没有足够的联系。另见 the relevant JLS .

你问它是否是类型安全的。我的回答是它类型安全,这表明您的设计存在缺陷。考虑您对 Comparable<T> 的虚构实现界面,我在其中添加了两个字段:

public static class Foo<T> implements Comparable<Foo<T>> {

  private T pState;
  private String state;

  @Override
  public int compareTo(Foo<T> other) {
    return 0;
  }
}

你总是返回0 ,所以问题没有被发现。但是当您尝试让它变得有用时,您有两个选择:

  1. 比较字符串字段
  2. 比较 T成员(member)

String字段始终是 String ,所以你并没有真正从类型变量中受益 T .另一方面,T没有其他可用的类型信息,所以在 compareTo()你只能处理一个普通对象,类型参数也是无用的。您可以通过实现 Comparable<Foo<?>> 实现完全相同的功能

关于带有通配符的 Java 泛型在 Eclipse 中编译,但在 javac 中不编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13534946/

相关文章:

c# - .NET 泛型 - 比较两个列表并过滤 : best practice

java - 使用 spring mongodb 创建一个具有差异的 ProjectionOperation

java - Java中的基数转换

java - 转换到一个抽象类......这怎么可能?

Eclipse Tomcat 日志文件

java - 升级 Eclipse 时快速远程重新安装所需插件

java - JFrame 停止接收鼠标事件

java - Eclipse/GGTS 在 Mac OS X 上与 Java 7 崩溃?

java - 将通用列表转换为数组

Java 方法声明返回带有引用的列表泛型