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

我有以下课程:

public class Obj 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 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 cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,java.lang.Object -> getObject()

它不会发生在Eclipse中,据我所知,它使用与我的Ant构建相同的JDK。 我已经看到了关于Sun编译器问题的SO线程 ,但这似乎是针对动态声明类型的静态方法。

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

到目前为止,我发现的唯一原因就是像我这样使用我的方法:

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

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

它没有编译,因为你的代码对generics的期望过高 – >即, X部分:

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

在以下代码中:

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

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

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

您的问题的解决方案是将 X替换为Object:

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

并添加一个演员:

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

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

这是在Java SE 7中修复的虚拟错误 。

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

 public  X get(String property) 

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

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

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

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

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

我刚刚在使用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.get(item, "foo");