Java 8'de bir Akış yayınlamak mümkün mü?


160

Java 8'de akış yayınlamak mümkün mü? Diyelim bir nesne listesi var, tüm ek nesneleri filtrelemek için böyle bir şey yapabilirim:

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

Bundan sonra, eğer müşterilerle bir şey yapmak istersem, her birini atmam gerekir:

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

Bu biraz çirkin gözüküyor. Bir akışın tamamını farklı bir türe yayınlamak mümkün mü? Gibi dökme Stream<Object>a Stream<Client>?

Lütfen böyle bir şey yapmanın muhtemelen kötü tasarım anlamına geleceğini göz ardı edin. Bilgisayar bilimi dersimde böyle şeyler yapıyoruz, bu yüzden java 8'in yeni özelliklerine bakıyordum ve bunun mümkün olup olmadığını merak ettim.


3
Java çalışma zamanı açısından, iki Akış türü zaten aynıdır, bu nedenle herhangi bir döküm gerekmez. Hüner derleyiciyi gizlice geçmektir. (Yani, bunun herhangi bir anlam ifade ettiğini varsayarsak.)
Hot Licks

Yanıtlar:


283

Kutunun dışında yapmanın bir yolu olduğunu sanmıyorum. Muhtemelen daha temiz bir çözüm:

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

veya, Yorum önerilen olarak şunu kullanabilirsiniz castyöntemi - Eski rağmen okumak daha kolay olabilir:

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

Aradığım şey buydu. Ben Müşteriye döküm mapbir dönecekti göz ardı sanırım Stream<Client>. Teşekkürler!
Phiction

Yeni nesil türün spagetti koduna
girme

@LordOfThePigs Evet, kodun daha net olup olmadığından emin olmasam da çalışıyor. Bu fikri yanıtıma ekledim.
assylias

38
Stream.of(objects).filter(Client.class::isInstance).[...]
ExampleOf


14

Ggovan'ın yanıtı boyunca, bunu şu şekilde yapıyorum:

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

Bu yardımcı işlevi kullanarak:

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

10

Partiye geç, ama bence yararlı bir cevap.

flatMap bunu yapmanın en kısa yolu olurdu.

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

Bir oise Client, tek bir öğeyle bir Akış oluşturun, aksi takdirde boş akışı kullanın. Bu akışlar daha sonra a Stream<Client>.


Bunu uygulamaya çalıştım, ancak sınıfımın "denetlenmeyen veya güvenli olmayan işlemler kullandığını" belirten bir uyarı aldım - bu beklenen bir şey mi?
aweibell

Maalesef evet. Eğer operatör if/elseyerine bir kullanmak ?:olsaydı hiçbir uyarı olmazdı. Uyarıyı güvenle kaldırabileceğinizden emin olabilirsiniz.
ggovan

3
Aslında bu daha uzun, Stream.of(objects).filter(o->o instanceof Client).map(o -> (Client)o)hatta daha uzun Stream.of(objects).filter(Client.class::isInstance).map(Client.class::cast).
Didier L

4

Bu biraz çirkin gözüküyor. Bir akışın tamamını farklı bir türe yayınlamak mümkün mü? Gibi dökme Stream<Object>a Stream<Client>?

Hayır, bu mümkün olmazdı. Bu, Java 8'de yeni değildir. Bu, jeneriklere özgüdür. Bir List<Object>süper türü değil List<String>, sadece bir döküm olamaz bu yüzden, List<Object>bir karşı List<String>.

Burada da benzer bir sorun var. Sen döküm olamaz Stream<Object>için Stream<Client>. Tabii dolaylı olarak şöyle atabilirsiniz:

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

ancak bu güvenli değildir ve çalışma zamanında başarısız olabilir. Bunun altında yatan neden, Java'daki jeneriklerin silme kullanılarak uygulanmasıdır. Yani, hangi tür Streamçalışma zamanında olduğu hakkında hiçbir tür bilgi mevcut değildir . Herşey sadece Stream.

BTW, yaklaşımınızın nesi yanlış? Bana iyi geliyor.


2
@DR Generics, reification C#kullanılarak, Java'da ise silme kullanılarak uygulanır. Her ikisi de altta yatan farklı bir şekilde uygulanmaktadır. Yani her iki dilde de aynı şekilde çalışmasını bekleyemezsiniz.
Rohit Jain

1
@DR Silme işleminin yeni başlayanlar için Java'daki jenerik kavramını anlaması için birçok sorun yarattığını anlıyorum. Ve C # kullanmadığım için karşılaştırma hakkında fazla ayrıntıya giremiyorum. Ancak IMO'nun bu şekilde uygulanmasının ardındaki tüm motivasyon, JVM uygulamasında büyük değişikliklerden kaçınmaktı.
Rohit Jain

1
Neden "çalışma zamanında kesinlikle başarısız olur"? Dediğiniz gibi (genel) tür bilgisi yok, bu yüzden çalışma zamanının kontrol edeceği hiçbir şey yok. Bu olabilir belki yanlış türleri beslendiği taktirde, çalışma zamanında başarısız, ama bu her ne ise hakkında hayır "kesinlik" yoktur.
Hot Licks

1
@RohitJain: Java'nın genel konseptini eleştirmiyorum, ancak bu tek sonuç hala çirkin kod yaratıyor ;-)
DR

1
@DR - Java jenerikleri git-go'dan çirkin. Çoğunlukla sadece C ++ şehvet özelliği.
Hot Licks
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.