【问题标题】:incompatible types: inference variable E#1 has incompatible upper bounds Enum<E#2>不兼容的类型:推理变量 E#1 具有不兼容的上限 Enum<E#2>
【发布时间】:2017-05-24 18:55:08
【问题描述】:

为什么在我构建项目时会引发错误(但在运行单元测试时不会)...

protected <E extends Enum<E>> E getEnum(JSONObject jsonObject, String propertyName, Type type)
{
    String jsonString = jsonObject.optString(propertyName, null);
    return new GsonBuilder().create().fromJson(jsonString, type);
}

...虽然这完全有效(注意区别 - 最后一个参数 - 未使用!):

protected <E extends Enum<E>> E getEnum(JSONObject jsonObject, String propertyName, Type type, Class<E> clazz)
{
    String jsonString = jsonObject.optString(propertyName, null);
    return new GsonBuilder().create().fromJson(jsonString, type);
}

错误:

   warning: [options] bootstrap class path not set in conjunction with -source 1.7 C:\Projects\bla\bla\bla.java:32: error: incompatible types: inference variable E#1 has incompatible upper bounds Enum<E#2>,Termination
    Termination termination = getEnum(jsonObject, "termination", Termination.TYPE);                                                         
    where E#1,E#2 are type-variables:
      E#1 extends Enum<E#1> declared in method <E#1>getEnum(JSONObject,String,Type)
      E#2 extends Termination

我应该怎么做才能改善这一点?

编辑:

作为附加信息:这是我调用该方法的方式(使用第二个示例,错误消息中已经显示了第一个示例):

Termination termination = getEnum(jsonObject, "termination", Termination.TYPE, Termination.class).

这是该枚举的简化版本:

public enum Termination
{
    @SerializedName("endDate")END_DATE, 
    @SerializedName("recurrenceCount")COUNT,
    @SerializedName("forever")FOREVER;

    public static final java.lang.reflect.Type TYPE = new TypeToken<Termination>(){}.getType();
}

现在我明白了——由于类型推断——我显然需要定义类类型,如第二个示例所示。然而,这不是我的问题。我想知道 1:为什么 Gson 库能够和我做的完全一样(据我从下面的代码示例中可以看出),以及 2:为什么在大多数情况下不能编译,同时运行单元测试没问题。

Gson 示例代码(两个示例中调用的方法):

@SuppressWarnings("unchecked")
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
  if (json == null) {
    return null;
  }
  StringReader reader = new StringReader(json);
  T target = (T) fromJson(reader, typeOfT);
  return target;
}

编辑 2:

显然,当我省略“扩展枚举”部分时,编译器不再抱怨,所以我不需要将类型作为参数传递。 (这看起来更像 Gson 示例,这就是它为该代码编译但在第一个示例中没有为我编译的原因)。所以我的第一个例子现在变成了:

protected <E> E getEnum(JSONObject jsonObject, String propertyName, Type type)
{
    String jsonString = jsonObject.optString(propertyName, null);
    return new GsonBuilder().create().fromJson(jsonString, type);
}

当然我还是想扩展 E 以确保该方法只能用于返回枚举。

剩下的问题:

  1. 如何改进代码来解决这个问题?
  2. 为什么在不传递具体类型作为参数的情况下它可以工作,为什么在扩展 Enum 时它不能工作?
  3. 为什么编译器在运行单元测试时不抱怨原始的第一个示例?

【问题讨论】:

    标签: java generics enums


    【解决方案1】:

    第一个示例中的类型参数无法知道要替换的具体类型。参数列表中必须有信息来解决这个问题。在第二个示例中,Class&lt;E&gt; 参数提供了必要的信息。

    【讨论】:

    • 听起来很合理,但我之前见过一些例子,我不需要将类类型作为参数传递,只需将泛型类型定义为返回类型,这显然就足够了。 ...如果不是,那么 GsonBuilder 怎么知道? fromJson 方法看起来与我的第一个示例非常相似。 ...我猜它正在从 Type 参数中读取具体类型。那么我该怎么做呢?尽管如此,这并不能解释为什么 'fromJson' 不需要 Class 参数来提供必要的信息,就像您在我的示例中暗示的那样。
    • 研究类型推断。必须有一些东西来告诉编译器要推断什么。您没有向我们展示完整的示例,因此我们不知道该方法是如何使用的。也许有一个提供信息的分配上下文,但您展示的内容对第一个示例没有任何作用。
    • 我已经对我的问题进行了两次编辑,并找到了(部分)我正在寻找的答案。如果您能调查一下,并再次回答(新)问题,我将不胜感激。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2020-05-04
    相关资源
    最近更新 更多