Java 8でストリームをキャストすることは可能ですか?


160

Java 8でストリームをキャストすることは可能ですか?オブジェクトのリストがあるとしましょう。次のようにして、追加のオブジェクトをすべて除外できます。

Stream.of(objects).filter(c -> c instanceof Client)

この後、クライアントと何かをしたい場合は、それぞれをキャストする必要があります。

Stream.of(objects).filter(c -> c instanceof Client)
    .map(c -> ((Client) c).getID()).forEach(System.out::println);

これは少し醜く見えます。ストリーム全体を別のタイプにキャストすることは可能ですか?にキャストStream<Object>するようなStream<Client>

このようなことはおそらく悪いデザインを意味するという事実を無視してください。私はコンピューターサイエンスのクラスでこのようなことをしているので、Java 8の新機能を調べていて、それが可能かどうか知りたいと思っていました。


3
Javaランタイムの観点から見ると、2つのStreamタイプは同じなので、キャストは必要ありません。秘訣は、コンパイラを通り抜けることです。(つまり、そうすることが理にかなっていると想定しています。)
Hot Licks '19年

回答:


283

すぐにそれを行う方法はないと思います。よりクリーンな解決策は次のとおりです。

Stream.of(objects)
    .filter(c -> c instanceof Client)
    .map(c -> (Client) c)
    .map(Client::getID)
    .forEach(System.out::println);

または、コメントで提案されているように、castメソッドを使用できます。ただし、前者の方が読みやすいかもしれません。

Stream.of(objects)
    .filter(Client.class::isInstance)
    .map(Client.class::cast)
    .map(Client::getID)
    .forEach(System.out::println);

これは私が探していたものとほぼ同じです。それをクライアントにキャストするとmapが返されることを見落としていたと思いStream<Client>ます。ありがとう!
フィクション

興味深い新しい方法を+1しますが、新世代タイプ(水平ではなく、水平)のスパゲッティコードに入るリスクがあります
robermann

@LordOfThePigsはい、コードはより明確になるかどうかはわかりませんが、機能します。アイデアを回答に追加しました。
アッシリア14年

38
あなたはでのinstanceofのフィルターを「簡素化」ができますStream.of(objects).filter(Client.class::isInstance).[...]
ニコラスLabrot

矢印のない部分は本当に美しい<3
Fabich

14

ggovanの答えに沿って、私はこれを次のように行います。

/**
 * Provides various high-order functions.
 */
public final class F {
    /**
     * When the returned {@code Function} is passed as an argument to
     * {@link Stream#flatMap}, the result is a stream of instances of
     * {@code cls}.
     */
    public static <E> Function<Object, Stream<E>> instancesOf(Class<E> cls) {
        return o -> cls.isInstance(o)
                ? Stream.of(cls.cast(o))
                : Stream.empty();
    }
}

このヘルパー関数の使用:

Stream.of(objects).flatMap(F.instancesOf(Client.class))
        .map(Client::getId)
        .forEach(System.out::println);

10

パーティーに遅れるが、それは有用な答えだと思う。

flatMap それを行うための最短の方法でしょう。

Stream.of(objects).flatMap(o->(o instanceof Client)?Stream.of((Client)o):Stream.empty())

場合oされClient、その後、単一の要素でストリームを作成し、それ以外の場合は空のストリームを使用しています。その後、これらのストリームはにフラット化されますStream<Client>


これを実装しようとしましたが、私のクラスが「チェックされていない、または安全でない操作を使用している」という警告が表示されました。これは予期されることですか?
aweibell 2014

残念ながらそうです。演算子if/elseではなくを使用した場合、?:警告は表示されません。安全に警告を抑制できるので安心してください。
ggovan '10 / 12/10

3
実はこれは、より長いですStream.of(objects).filter(o->o instanceof Client).map(o -> (Client)o)かさえもStream.of(objects).filter(Client.class::isInstance).map(Client.class::cast)
Didier L

4

これは少し醜く見えます。ストリーム全体を別のタイプにキャストすることは可能ですか?にキャストStream<Object>するようなStream<Client>

いいえ、それは不可能でしょう。これはJava 8の新機能ではありません。これはジェネリックに固有です。List<Object>スーパータイプではないList<String>あなただけのキャストすることはできませんので、List<Object>List<String>

同様の問題がここにあります。あなたはキャストすることはできませんStream<Object>Stream<Client>。もちろん、次のように間接的にキャストできます。

Stream<Client> intStream = (Stream<Client>) (Stream<?>)stream;

しかし、これは安全ではなく、実行時に失敗する可能性があります。これの根本的な理由は、Javaのジェネリックが消去を使用して実装されていることです。したがって、Stream実行時にそれがどのタイプであるかについてのタイプ情報はありません。すべてがただStreamです。

ところで、あなたのアプローチの何が問題になっていますか?私には元気に見えます。


2
@DR Genericsは具体C#化を使用して実装されていますが、Javaでは消去を使用して実装されています。どちらも異なる方法で実装されています。したがって、両方の言語で同じように機能することは期待できません。
Rohit Jain 14年

1
@DR消去は初心者がJavaのジェネリックスの概念を理解するために多くの問題を引き起こすことを理解しています。また、C#を使用していないため、比較について詳しく説明することはできません。しかし、このようにIMOを実装する背後にある全体的な動機は、JVM実装の大きな変更を回避することでした。
Rohit Jain 14年

1
「実行時に確実に失敗する」のはなぜですか?あなたが言うように、(一般的な)タイプ情報はないので、ランタイムがチェックするものは何もありません。間違ったタイプがフィードされた場合、実行時に失敗する可能ありますが、それについての「確実性」はまったくありません。
Hot Licks

1
@RohitJain:Javaの一般的な概念を批判しているわけではありませんが、この単一の結果が依然として醜いコードを作成しています;-)
DR

1
@DR-Javaジェネリックはgit-goの醜いものです。ほとんどの場合、C ++機能の欲望だけです。
Hot Licks、2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.