纠正上界类类型可迭代和上界通配符List字段迭代器类型

时间:2018-11-18 08:53:03

标签: java generics

我正在尝试找到正确的泛型和通配符安排以使其正常工作。类型已更改,所有其他代码都已删除,以使其尽可能简单。

在这里,我有一个通用类,其上限为Number,实现了Iterable。它包含通用类型的List

我想传入任何List子类的Number,然后从Iterator字段返回该类型的List

这是显然不起作用的基本想法。

class GenericQuestion<T extends Number> implements Iterable<T> {
    List<T> numbers;

    GenericQuestion(List<T> number) {
        this.numbers = number;
    }

    public Iterator<T> iterator() {
        return numbers.iterator();
    }

    static void main() {
        for (Number n : new GenericQuestion<Number>(new ArrayList<Integer>())) { // <---- error
            System.out.println(n.byteValue());
        }
    }
}

这给出了预期的错误

incompatible types: ArrayList<Integer> cannot be converted to List<Number>

救援的上限通配符!差不多...

class GenericQuestion<T extends Number> implements Iterable<T> {
    List<? extends T> numbers;

    GenericQuestion(List<? extends T> number) {
        this.numbers = number;
    }

    public Iterator<T> iterator() {
        return numbers.iterator(); // <----- error
    }

    static void main() {
        for (Number n : new GenericQuestion<Number>(new ArrayList<Integer>())) {
            System.out.println(n.byteValue());
        }
    }
}

现在,我陷入了迭代器类型冲突的困境。

incompatible types: Iterator<CAP#1> cannot be converted to Iterator<T>
  where T is a type-variable:
    T extends Number declared in class GenericQuestion
  where CAP#1 is a fresh type-variable:
    CAP#1 extends T from capture of ? extends T

从逻辑上来说,现在我很确定将强制返回的迭代器强制转换为(Iterator<T>)类型是绝对安全的, 但是我宁愿以“正确”的方式做到这一点。我已经尝试过各种方法。这是又一次绝望的尝试,仍然没有用。

class GenericQuestion<T extends Number> implements Iterable<T> {
    List<T> numbers;

    <U extends Number> GenericQuestion(List<U> number) {
        this.numbers = number; // <----- error
    }

    public Iterator<T> iterator() {
        return numbers.iterator();
    }

    static void main() {
        for (Number n : new GenericQuestion<Number>(new ArrayList<Integer>())) {
            System.out.println(n.byteValue());
        }
    }
}

错误。

incompatible types: List<U> cannot be converted to List<T>
  where U,T are type-variables:
    U extends Number declared in constructor <U>GenericQuestion(List<U>)
    T extends Number declared in class GenericQuestion

我认为也许可以使用辅助功能来弥补差距,但是我的尝试并没有任何运气。

这似乎并不难。强制铸造真的是要走的路吗?还是有一个通用的声明可以使所有这些协同工作?

3 个答案:

答案 0 :(得分:1)

对测试进行一次小改动,以使其不试图强制使用GenericQuestion<Number>(而是让推断类型):

for (Number n : new GenericQuestion<>(new ArrayList<Integer>())) {

您还可以更改List可迭代类型的声明,例如:

class GenericQuestion<T extends Number, L extends List<T>>
        implements Iterable<T> {

    private L numbers;

    GenericQuestion(L number) {
        this.numbers = number;
    }

答案 1 :(得分:1)

如果您想在T上使用变量,我认为您必须在某处有 some 类型的不安全代码。

引起此限制的原因是Iterable<T>。它需要一种方法Iterator<T> iterator()。您不确定Iterator<? extends T>是否可以转换为Iterator<T>,因为唯一可行的情况是运行时Iterator<? extends T>Iterator<T>。您可能已经了解了一个事实,即使A<T>也无法将A<U>转换为T extends U

相反,如果您创建了自己的MyIterable<T>界面,则可以执行以下操作:

class GenericQuestion<T extends Number> implements MyIterable<T> {
    List<? extends T> numbers;

    GenericQuestion(List<? extends T> number) {
        this.numbers = number;
    }

    public Iterator<? extends T> iterator() {
        return numbers.iterator();
    }

    static void main() {
        GenericQuestion<Number> q = new GenericQuestion<>(new ArrayList<Integer>());
        Iterator<? extends Number> iter = q.iterator();
        System.out.println(iter.next().byteValue());
    }
}

interface MyIterable<T> {
    Iterator<? extends T> iterator();
}

但是您可以看到,MyIterable不能与for循环一起使用。

因此,您要么不允许new GenericQuestion<Number>(new ArrayList<Integer>()),要么编写类型不安全的代码。

答案 2 :(得分:0)

我发现的另一个解决方案是用基本类型的迭代器包装通配符迭代器。从技术上讲,我怀疑这是应该如何处理这种情况。

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myModule">
  <div ng-controller="myController">
    <div class="item" ng-repeat="room in rooms">
      {{ room }}
      <input type="text" name="{{ item.key }}" class="form-control" ng-model="item.data['section' + room]">
    </div>
  </div>
</div>