为什么编译器声明没有唯一的最大实例存在?

时间:2011-04-14 15:56:09

标签: java generics compiler-construction compiler-errors javac

我有以下课程:

public class Obj<T> extends BaseModel {

    public static final String OBJECT = "object";

    public Obj(T object) {
        setObject(object);
    }

    public T getObject() {
        return get(OBJECT);
    }

    public void setObject(T object) {
        set(OBJECT, object);
    }
}

和...

/** This is a 3rd party library class **/
public class BaseModel implements ModelData, Serializable {
  //...members and stuff...

  @SuppressWarnings({"unchecked", "rawtypes"})
  public <X> X get(String property) {
    X obj = null;
    if (start > -1 && end > -1) {
      Object o = map.get(property.substring(0, start));
      String p = property.substring(start + 1, end);
      if (o instanceof Object[]) {
        obj = (X) ((Object[]) o)[Integer.valueOf(p)];
      } else if (o instanceof List) {
        obj = (X) ((List) o).get(Integer.valueOf(p));
      } else if (o instanceof Map) {
        obj = (X) ((Map) o).get(p);
      }
    } else {
      obj = (X) map.get(property);
    }
    return obj;
  }
}

编译时,我收到以下错误。

type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,java.lang.Object -> getObject()

它不会发生在Eclipse中,据我所知,它使用与我的Ant构建相同的JDK。我见过SO thread about the Sun compiler issue,但这似乎是静态方法在运行时声明类型。

为什么我会收到此错误,更重要的是,我该如何解决此问题?

到目前为止,我发现的唯一原因就是在我的方法中投射:

@SuppressWarnings({"unchecked"})
public T getObject() {
    return (T) get(OBJECT); //yuck
}

告诉我我的裂缝,这是正确的方法是可以接受的。

4 个答案:

答案 0 :(得分:21)

这是在Java SE 7中修复的虚拟bug

答案 1 :(得分:17)

它无法编译,因为您的代码对泛型的期望过高 - &gt;即,&lt; X> X部分:

public <X> X get(String property) { ... }

在以下代码中:

public T getObject() {
  return get(OBJECT);
}

你必须记住,在编译器实际开始编译Java代码之前,泛型总是“展开”。这是一个预处理步骤。

在您的情况下,编译器不知道在编译时使用什么来替换X 。编译器需要确定X的类型,因为它需要针对T进行检查以验证代码。因此错误......

您的问题的解决方案是替换&lt; X>带对象的X:

public Object get(String property) { ... }

并添加一个演员:

public T getObject() {
  return (T) get(OBJECT);
}

您将在编译时获得未经检查的转换警告,但您的代码将被编译(所以是的,您的解决方法是有效的。)

答案 2 :(得分:4)

通常从该方法的参数隐式推断出方法类型参数。但请注意,get在参数和类型参数之间没有明确的关系:

public <X> X get(String property)

类型推断是通常的路径,但也可以使用显式类型参数调用方法,就像类一样。格式大致遵循声明的格式,因此在Obj中你可以有

public T getObject() {
    return super.<T>get(OBJECT);
}

您也可以直接使用<Object>,但您仍然必须使用未经检查的强制转换才能将其恢复为T。请注意,显式参数需要限定符,通常是类的实例名称。由于您的示例使用了超类的方法,因此它的引用是通过super隐式的。

这并不能解决在非泛型类(<X> X get)中应用泛型方法(BaseModel)的根本问题。请注意,库中的代码会将强制类型转换为类型参数。这种风格确实是将通用功能反向移植到非通用Java代码中的解决方案之一。看起来他们试图将其隐藏在库用户之外,但由于他们没有对类进行泛化,因此无法从实例中推断出类型(即您真的想要Obj<T> extends BaseModel<T>)。

[编辑:纠正并解释显式方法类型参数]

答案 3 :(得分:1)

我刚刚在使用Apache Pivot的项目中遇到了类似的问题。客户端代码充斥着如下行:

boolean foo = org.apache.pivot.json.JSON.get(item, "foo");

代码将在Eclipse中编译,但不能从命令行使用Maven或javac。它似乎是Bug 6302954,但在更新到最新的JDK之后我仍然看到它。

由于JSON类是由Pivot提供的,所以我不能在我自己的源代码树中修改它(分支库不是这个项目的选项)

对我有用的解决方案来自错误报告中的第一个回复,将代码更改为:

boolean foo = org.apache.pivot.json.JSON.<Boolean>get(item, "foo");