【问题标题】:How to cast an Iterator of a class to an Iterator of a subclass thereof?如何将一个类的迭代器转换为其子类的迭代器?
【发布时间】:2012-07-14 16:29:04
【问题描述】:

我试图将一个类的迭代器转换为该类的子类的迭代器。这给了我一个“不可转换的类型”错误。为什么这是不可能的,最优雅的解决方法是什么? (或者,如果是这样,为什么这是一个坏主意?)

在这种情况下,使用 for-each 循环不是解决方案:我正在尝试实现 iterator(),最简单的方法是返回我的班级字段之一的 iterator(),但是那个没有确切要求的类型。我无法更改我的iterator()either 的签名。

public interface SomeoneElsesInterface {
    public Iterator<SomeoneElsesInterface> iterator();
}

public abstract class MyAbstractClass implements SomeoneElsesInterface {
    final MyAbstractClass[] things;
    public MyAbstractClass(SomeoneElsesInterface... things) {
         this.things = (MyAbstractClass[]) things;
    }
}

public class MyClass extends MyAbstractClass {
    public MyClass(MyAbstractClass thing1, MyAbstractClass thing2) {
        super(thing1, thing2);
    }

    public Iterator<SomeoneElsesInterface>() {
        return (Iterator<SomeoneElsesInterface>) Arrays.asList(things).iterator();
    }
}

当然,我可以只更改things 的类型。但是,在这种情况下,我需要在其他地方进行大量演员表。我知道我的构造函数不会被不是MyAbstractClasss 的对象调用,但无论如何我都无法更改接口。

【问题讨论】:

  • 类的类型和所包含字段的类型之间的关系是什么(如果有的话)?你能给我们看一些代码吗?

标签: java generics collections


【解决方案1】:

这看起来就像使用explicit type argument specification 一样简单:

public class MyClass extends MyAbstractClass {
    // ...

    public Iterator<SomeoneElsesInterface> iterator() {
      return Arrays.<SomeoneElsesInterface>asList(things).iterator();
    }
}

问题在于Arrays#asList() 推断您需要List&lt;MyAbstractClass&gt; 类型的列表,这将产生Iterator&lt;MyAbstractClass&gt; 类型的迭代器。由于Iterator 的类型参数不是协变,因此您不能在需要Iterator&lt;SomeoneElsesInterface&gt; 的地方提供Iterator&lt;MyAbstractClass&gt;。通过强制Arrays#asList() 创建一个List&lt;SomeoneElsesInterface&gt; 类型的列表,如上所示,您还可以从对Iterable#iterator() 的调用中返回预期的迭代器类型。

SomeoneElsesInterface 的作者更愿意将其iterator() 方法的返回类型指定为Iterator&lt;? extends SomeoneElsesInterface&gt;

【讨论】:

    【解决方案2】:

    如果将Arrays.asList(things) 更改为Arrays.asList((SomeoneElsesInterface[]) things) 会怎样?一旦数组被转换为正确的类型,List 和 Iterator 应该跟随。

    【讨论】:

      【解决方案3】:

      我认为根据您的问题,您正在尝试做这样的事情:

      Iterator<Object> original = ...
      Iterator<String> converted = (Iterator<String>)original;
      

      对吗?

      如果是这样,那很遗憾,这是不可能的。问题是original 可以包含不是Strings 的对象,因此允许该转换会破坏泛型合同,即converted 可能包含不是String 的东西。

      我不认为有一个优雅的解决方法。

      你说实现iterator() 的最简单方法是返回一个实例字段的迭代器,所以我猜你有这样的东西:

      class IterableThing implements Iterable<Foo> {
          private Collection<Bar> someStuff;
      
          public Iterator<Foo> iterator() {
              return (Iterator<Foo>)someStuff.iterator();
          }
      }
      
      class Bar {
      }
      class Foo extends Bar {
      }
      

      如果someStuff 可以保证只包含Foo 的实例,那么您能否将someStuff 声明为Collection&lt;Foo&gt; 而不是Collection&lt;Bar&gt;?如果不是,那么只返回someStuff 的迭代器就没有任何意义,因为它可能包含不是Foo 的东西。

      我想您需要考虑您实际上可以做出哪些保证。如果您不能保证someStuff 只包含Foos,那么您可能必须维护自己的状态,或者按需过滤someStuff 的内容。

      编辑:您已经用代码更新了您的问题。太棒了。

      所以看起来您实际上是在尝试在该类型的超类上返回一个迭代器。这让事情变得容易多了。

      在您的特定情况下,您可能可以通过以下方式解决它:

      return Arrays.<SomeoneElsesInterface>asList(things).iterator();
      

      它会产生一些警告,但没关系,因为你知道你已经保证了类型安全。

      【讨论】:

      • 基本上,是的。但是在我的情况下,original 实际上是SubclassIterator,我希望converted 成为Subclass 的超类的Iterator。因此,我可以保证 original 只包含(传递)类实例的元素。
      【解决方案4】:

      使用for-each 循环而不是Iterator

      for-each 是从 Java 1.5

      引入的

      查看此链接了解更多详情:

      http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html

      【讨论】:

        【解决方案5】:

        Java for-each 示例(补充 Kumar 答案):

        List<String> strings = new ArrayList<String>();
        strings.add( "one" );
        strings.add( "two" );
        strings.add( "three" );
        
        for ( String item : strings ) {
            System.out.println( item );
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-18
          • 2020-01-03
          • 2011-03-24
          • 1970-01-01
          • 2010-10-03
          • 1970-01-01
          • 2020-02-25
          • 1970-01-01
          相关资源
          最近更新 更多