【问题标题】:Java Type Erasure, Generic Lists with Unchecked AssignmentJava 类型擦除,带有未检查分配的通用列表
【发布时间】:2018-10-31 22:37:50
【问题描述】:

目前我正在编写一个程序,其中包含以下语句。

List<BaseballStatistic> q = BaseballStatistic.FIND.where().eq("teamID", "CHN").query();

这里,它抱怨

未经检查的分配:“java.util.List”到“java.util.List”。原因:'BaseballStatistic.FIND.where().eq("teamID", "CHN")' 是原始类型,所以查询的结果被删除了更多...

我有一个看起来像这样的界面

public interface Query<T> {
  ...
  List<T> execute();
}

然后是实现这个接口的抽象类

public abstract class AbstractQuery<T> implements Query<T> {
  Statement _statement = null;
  String _tableName;
  List<Clause> _clauses;
  Class<T> _type;

AbstractQuery(Class<T> type) {
  _type = type;
  _clauses = new ArrayList<>();
  _tableName = type.getAnnotation(Table.class).name();
}

...
public abstract List<T> execute();
}

最后是一个具体的实现:

public class SimpleQuery<T> extends AbstractQuery<T> {

public SimpleQuery(Class<T> type) {
  super(type);
}

其中包含以下 .query 函数,如下所示:

@Override
public List<T> execute() {
try {
  JSONObject jsonObject = Peanut.getClient().listStatistics(buildQuery());

  if (jsonObject == null || !jsonObject.has("results")) {
    return Collections.emptyList();
  }

  JSONArray columnNames = jsonObject.getJSONArray("columns");

  Map<String, Integer> columnNameMap = new HashMap<>();
  for (int i = 0; i < columnNames.length(); i++) {
    columnNameMap.put((String) columnNames.get(i), i);
  }

  JSONArray results = jsonObject.getJSONArray("results");

  List<T> ts = new ArrayList<>();
  for (int i = 0; i < results.length(); i++) {
    JSONArray result = results.getJSONArray(i);
    T t = _type.newInstance();
    for (Field field : ObjectUtils.getFieldsUpTo(t.getClass(), PinotModel.class)) {
      if (field.getAnnotation(Column.class) == null) {
        continue;
      }

      Object obj = ObjectUtils.getDefaultValue(field.getType());
      String columnName = field.getAnnotation(Column.class).name();

      if (columnNameMap.containsKey(columnName)) {
        int idx = columnNameMap.get(columnName);
        field.setAccessible(true);
        field.set(t, ObjectUtils.convertObject(obj, result.get(idx)));
      }
    }
    ts.add(t);
  }
  return ts;
} catch (Exception e) {
  // TODO: Throw Peanut specific error.
  Peanut.LOG.error(e);
  return Collections.emptyList();
}
}

似乎在这里,在编译时,返回的列表丢失了导致警告的类型。如果我将原始变量声明更改为 List 警告将离开,这是有道理的。

是否存在这个问题,或者是否存在更大的基本问题?

编辑:

调用execute的查询函数在这里

 public List<T> query() {
   return _query.execute();
 }

SimpleQuery和BaseballStatistic.Find的关系如下。

@Table(name = "baseballStats")
public class BaseballStatistic extends PinotModel {

  public static final Find FIND = new Find<BaseballStatistic (BaseballStatistic.class) { };
...

PinotModel 看起来像

public class PinotModel {
  public static class Find<T> {
    private final Class<T> type;

  protected Find(Class<T> type) {
    this.type = type;
  }

  public Query select(String... s) {
    return new SimpleQuery<T>(type).select(s);
  }

  public Clause where() {
    return new SimpleQuery<T>(type).where();
  }

  public Clause limit(Integer n) {
    return new SimpleQuery<T>(type).limit(n);
  }

  public Clause top(Integer n) {
    return new SimpleQuery<T>(type).top(n);
  }

  public Clause orderBy(String columnName, Order o) {
    return new SimpleQuery<T>(type).orderBy(columnName, o);
  }

  public String tableName() {
    return new SimpleQuery<T>(type).getTableName();
  }
 }
}

【问题讨论】:

  • 类型擦除本身是一个很大的基本问题,但这完全是一个单独的讨论。我只是给它添加一个@SuppressWarnings,因为它有一个合理的解释。
  • 由于警告确实在List&lt;BaseballStatistic&gt; q = BaseballStatistic.FIND.where().eq("teamID", "CHN").query(); 上,我反对将@SuppressWarnings 放在上面,因为这段代码最终应该属于一个库
  • 您不需要添加那么多信息。例如,execute 的实现现在并不重要。我们需要的是方法签名、返回类型和变量类型。例如:public static final Find FIND 为什么在这里使用 Find 的原始类型?这是另一个原始类型 public Query select(...) .. 你的 IDE 不应该警告你吗?
  • 这不是问题,问题是“为什么它是原始类型”?即你为什么在那里使用Find 而不是Find&lt;BaseballStatistic&gt;Queryselect 的返回类型相同。可以理解,Intellij 会警告您注意 List&lt;BaseballStatistic&gt; q = BaseballStatistic.FIND.where().eq("teamID", "CHN").query(); 的不同类型,因为您在链接方法调用时已停止继承泛型类型。因此,Intellij 不再知道 query() 真正 在那个时候返回什么。
  • “我假设发生这种情况的原因是我没有在代码链的某个地方传递类型信息?”那是对的。当您的 IntelliJ 没有正确警告您此类代码时,请转到设置 -> 编辑器 -> 检查,然后转到 Java-> 代码样式问题 -> 原始类型可以是通用的。激活该检查。然后重新检查您的代码以获取原始类型/缺少的类型信息。

标签: java generics orm


【解决方案1】:

有 2 个地方缺少泛型类型参数。

  1. BaseballStatistic.FIND:

    public static final Find&lt;BaseballStatistic&gt; FIND = new Find&lt;BaseballStatistic&gt; (BaseballStatistic.class) { };

  2. PinotModel.select:

    public Query<T> select(String... s) { return new SimpleQuery<T>(type).select(s); }

您还缺少PinotModel.where() 上的类型参数。 Clause 还需要一个类型参数,包括 AbstractQuery._clauses 字段。

【讨论】:

    猜你喜欢
    • 2016-02-14
    • 1970-01-01
    • 2016-05-03
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多