9

クラスのイテレータをそのクラスのサブクラスのイテレータにキャストしようとしました。これにより、「変換不可能なタイプ」エラーが発生しました。なぜこれが不可能であり、それを回避するための最もエレガントな方法は何ですか?(あるいは、そうである場合、なぜそれは悪い考えですか?)

この場合、for-eachループを使用することは解決策ではありません。実装しようとしiterator()ています。これを行う最も簡単な方法iterator()は、クラスのフィールドの1つを返すことですが、そのフィールドには正確に必要なものがありません。タイプ。どちらの署名も変更できませんiterator()

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ます。ただし、その場合は他の場所でたくさんのキャストが必要になります。コンストラクターがs以外のオブジェクトで呼び出されないことは知っていますがMyAbstractClass、とにかくインターフェイスを変更することはできません。

4

5 に答える 5

5

これは、明示的な型引数指定を使用するのと同じくらい簡単に見えます:

public class MyClass extends MyAbstractClass {
    // ...

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

問題は、 typeのイテレータを生成するArrays#asList()type のリストが必要であると推測していることです。の型パラメータは共変ではないため、 が必要な場所にを指定することはできません。上記のように type のリストを強制的に作成すると、 への呼び出しから目的のイテレータ タイプが返されます。List<MyAbstractClass>Iterator<MyAbstractClass>IteratorIterator<MyAbstractClass>Iterator<SomeoneElsesInterface>Arrays#asList()List<SomeoneElsesInterface>Iterable#iterator()

の作成者は、そのメソッドSomeoneElsesInterfaceの戻り値の型を.iterator()Iterator<? extends SomeoneElsesInterface>

于 2012-07-15T17:48:29.850 に答える
5

あなたの質問から、あなたは次のようなことをしようとしていると思います:

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

あれは正しいですか?

もしそうなら、それは残念ながら不可能です。問題は、が s でoriginalはないオブジェクトを含む可能性がStringあるため、そのキャストを許可するとジェネリック コントラクトが壊れてしまうことです。つまり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と宣言できますか? そうでない場合、のイテレータを返すだけでは意味がありません。someStuffCollection<Foo>Collection<Bar>someStuffFoo

実際にどのような保証ができるかを考える必要があると思います。someStuffsのみが含まれていることを保証できない場合Fooは、おそらく独自の状態を維持するか、someStuffオンデマンドでコンテンツをフィルタリングする必要があります。

編集:コードで質問を更新しました。素晴らしい。

したがって、実際には型のスーパークラスに対してイテレータを返そうとしているようです。それは物事をずっと簡単にします。

あなたの特定のケースでは、おそらくこれで解決できます:

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

いくつかの警告が生成されますが、タイプ セーフが保証されていることがわかっているので問題ありません。

于 2012-07-15T17:26:23.653 に答える
1

for-eachの代わりにループを使用しますIterator

for-eachJava1.5から導入されました

詳細については、次のリンクを参照してください。

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

于 2012-07-15T17:09:04.477 に答える
0

に変えArrays.asList(things)たらArrays.asList((SomeoneElsesInterface[]) things)?配列が正しい型にキャストされると、List と Iterator が続きます。

于 2012-07-15T17:36:42.767 に答える
-1

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 );
}
于 2012-07-15T17:11:31.927 に答える