【问题标题】:Rectifying an upper bounded class type Iterable and upper bounded wildcard List field Iterator type纠正有界类类型可迭代和有界通配符列表字段迭代器类型
【发布时间】:2019-04-20 21:41:16
【问题描述】:

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

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

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

这是显然行不通的基本想法。

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&lt;T&gt;) 是完全安全的, 但我宁愿以“正确”的方式做到这一点。我已经尝试了各种各样的事情。这是另一个绝望的尝试,但仍然没有成功。

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

我认为也许辅助函数可以弥补差距,但我的尝试没有任何运气。

这似乎不应该那么难。强制施法真的可行吗?或者是否有一个通用声明可以让这一切一起工作?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    对您的测试稍作改动,以免它试图强制GenericQuestion&lt;Number&gt;(而是让推断类型):

    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;
        }
    

    【讨论】:

    • 我删除了代码以保持问题简单。完整实施中需要一个列表。
    • 使用GenericQuestion&lt;T extends Number, L extends List&lt;T&gt;&gt; 似乎是一种选择,尽管它要求此类的所有用户声明额外的通用参数。
    【解决方案2】:

    如果你想在T 上出现差异,我认为你必须在某处有一些类型的不安全代码。

    导致此限制的原因是Iterable&lt;T&gt;。它需要一个方法Iterator&lt;T&gt; iterator()。您不能确定您的Iterator&lt;? extends T&gt; 是否可以转换为Iterator&lt;T&gt;,因为唯一可能的情况是您的Iterator&lt;? extends T&gt; 在运行时是Iterator&lt;T&gt;。您可能已经明白,A&lt;T&gt; 无法转换为 A&lt;U&gt;,即使 T extends U

    如果您创建了自己的 MyIterable&lt;T&gt; 接口,则可以这样做:

    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&lt;Number&gt;(new ArrayList&lt;Integer&gt;()),要么编写类型不安全的代码。

    【讨论】:

    • 我最终采用了简单但技术上类型不安全的方法return (Iterator&lt;T&gt;)numbers.iterator();
    【解决方案3】:

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

    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 new Iterator<T>() {
                Iterator<? extends T> iter = numbers.iterator();
                public boolean hasNext() {
                    return iter.hasNext();
                }
    
                public T next() {
                   return iter.next();
                }
    
                public void remove() {
                    iter.remove();
                }
            };
        }
    
        static void main() {
            for (Number n : new GenericQuestion<Number>(new ArrayList<Integer>())) {
                System.out.println(n.byteValue());
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      • 1970-01-01
      • 2021-04-22
      • 2010-12-06
      • 1970-01-01
      相关资源
      最近更新 更多