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

问题描述 投票:0回答:4

我有以下课程:

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 中不会发生这种情况,据我所知,Eclipse 使用与我的 Ant 构建相同的 JDK。 我看过关于 Sun 编译器问题的 SO 线程,但这似乎是针对动态声明类型的静态方法。

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

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

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

告诉我我已经崩溃了,这是正确的方法是可以接受的。

java generics compiler-errors javac
4个回答
21
投票

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


17
投票

它无法编译,因为您的代码对泛型期望太多 -> 即:< X >中的

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

在以下代码中:

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

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

在您的情况下,编译器不知道在编译时使用什么来替换 X

。编译器需要确定 X 的类型,因为它需要对照 T 检查它以验证代码。因此出现错误... 解决您问题的方法是将

X 替换为对象:

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

并添加演员:

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

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


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>
)。

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


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");

	
© www.soinside.com 2019 - 2024. All rights reserved.