Est-il possible de diffuser un flux en Java 8?

160

Est-il possible de diffuser un flux en Java 8? Disons que j'ai une liste d'objets, je peux faire quelque chose comme ceci pour filtrer tous les objets supplémentaires:

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

Après cela, si je veux faire quelque chose avec les clients, j'aurais besoin de lancer chacun d'eux:

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

Cela a l'air un peu moche. Est-il possible de diffuser un flux entier vers un type différent? Comme jeté Stream<Object>un Stream<Client>?

Veuillez ignorer le fait que faire de telles choses signifierait probablement une mauvaise conception. Nous faisons des trucs comme ça dans mon cours d'informatique, donc je cherchais les nouvelles fonctionnalités de java 8 et j'étais curieux de savoir si c'était possible.

Phiction
la source
3
Du point de vue du runtime Java, les deux types de flux sont déjà les mêmes, donc aucune conversion n'est requise. L'astuce consiste à le faire passer devant le compilateur. (Autrement dit, en supposant que cela ait du sens de le faire.)
Hot Licks

Réponses:

283

Je ne pense pas qu'il y ait un moyen de faire cela hors de la boîte. Une solution éventuellement plus propre serait:

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

ou, comme suggéré dans les commentaires, vous pouvez utiliser la castméthode - la première peut être plus facile à lire cependant:

Stream.of(objects)
    .filter(Client.class::isInstance)
    .map(Client.class::cast)
    .map(Client::getID)
    .forEach(System.out::println);
assylies
la source
C'est à peu près ce que je recherchais. Je suppose que j'ai oublié que le diffuser au client en maprenverrait un fichier Stream<Client>. Merci!
Phiction
+1 nouvelles façons intéressantes, bien qu'elles risquent d'entrer dans du code spaghetti de type nouvelle génération (horizontal, pas vertical)
robermann
@LordOfThePigs Oui, cela fonctionne bien que je ne sois pas sûr que le code soit plus clair. J'ai ajouté l'idée à ma réponse.
assylias
38
Vous pourriez "simplifier" le filtre instanceOf avec:Stream.of(objects).filter(Client.class::isInstance).[...]
Nicolas Labrot
La partie sans flèche est vraiment belle <3
Fabich
14

Le long des lignes de la réponse ggovan , je fais ceci comme suit:

/**
 * 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();
    }
}

Utilisation de cette fonction d'assistance:

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

En retard à la fête, mais je pense que c'est une réponse utile.

flatMap serait le moyen le plus court de le faire.

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

Si oest un Clientalors créez un Stream avec un seul élément, sinon utilisez le flux vide. Ces flux seront ensuite aplatis en un fichier Stream<Client>.

ggovan
la source
J'ai essayé de l'implémenter, mais j'ai reçu un avertissement disant que ma classe "utilise des opérations non contrôlées ou non sécurisées" - est-ce normal?
aweibell
Malheureusement oui. Si vous utilisiez un if/elseplutôt que l' ?:opérateur, il n'y aurait aucun avertissement. Soyez assuré que vous pouvez supprimer l'avertissement en toute sécurité.
ggovan
3
En fait, c'est plus long Stream.of(objects).filter(o->o instanceof Client).map(o -> (Client)o)ou même Stream.of(objects).filter(Client.class::isInstance).map(Client.class::cast).
Didier L
4

Cela a l'air un peu moche. Est-il possible de diffuser un flux entier vers un type différent? Comme jeté Stream<Object>un Stream<Client>?

Non, ce ne serait pas possible. Ce n'est pas nouveau dans Java 8. Ceci est spécifique aux génériques. A List<Object>n'est pas un super type de List<String>, vous ne pouvez donc pas simplement convertir un List<Object>en un List<String>.

Le problème est similaire ici. Vous ne pouvez pas lancer Stream<Object>vers Stream<Client>. Bien sûr, vous pouvez le lancer indirectement comme ceci:

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

mais ce n'est pas sûr et peut échouer à l'exécution. La raison sous-jacente à cela est que les génériques en Java sont implémentés à l'aide de l'effacement. Par conséquent, aucune information de type n'est disponible sur le type de Streamcelui-ci au moment de l'exécution. Tout est juste Stream.

BTW, quel est le problème avec votre approche? Ça me va.

Rohit Jain
la source
2
@DR Generics in C#est implémenté à l'aide de la réification, tandis qu'en Java, il est implémenté à l'aide de l'effacement. Les deux sont mis en œuvre de manière différente sous-jacente. Vous ne pouvez donc pas vous attendre à ce que cela fonctionne de la même manière dans les deux langues.
Rohit Jain
1
@DR Je comprends que l'effacement pose de nombreux problèmes pour que les débutants comprennent le concept des génériques en Java. Et comme je n'utilise pas C #, je ne peux pas entrer dans les détails sur la comparaison. Mais toute la motivation derrière sa mise en œuvre de cette façon IMO était d'éviter des changements majeurs dans la mise en œuvre de la JVM.
Rohit Jain
1
Pourquoi cela «échouerait-il certainement à l'exécution»? Comme vous le dites, il n'y a pas d'informations de type (générique), donc rien à vérifier pour le runtime. Il pourrait éventuellement échouer à l'exécution, si les mauvais types sont transmis, mais il n'y a aucune "certitude" à ce sujet.
Hot Licks
1
@RohitJain: Je ne critique pas le concept générique de Java, mais cette seule conséquence crée toujours un code laid ;-)
DR
1
@DR - Les génériques Java sont moche depuis le git-go. Surtout juste la convoitise des fonctionnalités C ++.
Hot Licks