เป็นไปได้ไหมที่จะส่งกระแสข้อมูลใน 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 ทั้งสองประเภทสตรีมเหมือนกันอยู่แล้วดังนั้นจึงไม่จำเป็นต้องใช้ cast เคล็ดลับคือการแอบดูมันผ่านคอมไพเลอร์ (นั่นคือสมมติว่ามันเหมาะสมที่จะทำเช่นนั้น)
Hot Licks

คำตอบ:


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>ขอบคุณ!
Phiction

1 วิธีการใหม่ ๆ ที่น่าสนใจแม้ว่าพวกเขาจะมีความเสี่ยงที่จะได้รับในปาเก็ตตี้รหัสประเภทรุ่นใหม่ (แนวนอน, แนวตั้งไม่ได้)
robermann

@ LordOfThePigs ใช่มันใช้งานได้แม้ว่าฉันจะไม่แน่ใจว่ารหัสนั้นชัดเจนขึ้นหรือไม่ ฉันได้เพิ่มความคิดให้กับคำตอบของฉัน
assylias

38
คุณสามารถ "ลดความซับซ้อน" ตัวกรอง instanceOf ด้วย:Stream.of(objects).filter(Client.class::isInstance).[...]
Nicolas Labrot

ส่วนที่ไม่มีลูกศรนั้นสวยงามจริงๆ <3
Fabich

14

ตามคำตอบของ govanฉันทำสิ่งนี้ดังนี้

/**
 * 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

น่าเสียดายใช่ คุณใช้ตัวดำเนินการif/elseแทนตัว?:ดำเนินการแล้วจะไม่มีการเตือนใด ๆ มั่นใจได้ว่าคุณสามารถระงับคำเตือนได้อย่างปลอดภัย
ggovan

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 นี่เป็นข้อมูลเฉพาะของ generics A List<Object>ไม่ใช่ประเภทที่ยอดList<String>เยี่ยมดังนั้นคุณไม่สามารถส่ง a List<Object>ไปยัง a List<String>ได้

ปัญหาที่คล้ายกันคือที่นี่ คุณไม่สามารถทิ้งไปStream<Object> Stream<Client>แน่นอนคุณสามารถส่งมันทางอ้อมเช่นนี้:

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

แต่นั่นไม่ปลอดภัยและอาจล้มเหลวในขณะทำงาน เหตุผลพื้นฐานสำหรับสิ่งนี้คือข้อมูลทั่วไปใน Java ถูกนำมาใช้โดยใช้การลบ ดังนั้นจึงไม่มีข้อมูลประเภทที่เกี่ยวกับประเภทของStreamมันที่รันไทม์ Streamทุกอย่างเป็นเพียง

BTW เกิดอะไรขึ้นกับแนวทางของคุณ? ดูดีสำหรับฉัน


2
@DR Generics ในC#ถูกนำมาใช้โดยใช้การทำให้ใหม่ในขณะที่ใน Java มันถูกนำมาใช้โดยใช้การลบ ทั้งสองถูกนำมาใช้ในแบบต่าง ๆ พื้นฐาน ดังนั้นคุณไม่สามารถคาดหวังได้ว่ามันจะทำงานเหมือนกันทั้งสองภาษา
Rohit Jain

1
@DR ฉันเข้าใจว่าการลบออกทำให้เกิดปัญหามากมายสำหรับผู้เริ่มต้นที่จะเข้าใจแนวคิดของ generics ใน Java และเนื่องจากฉันไม่ได้ใช้ C # ฉันจึงไม่สามารถอธิบายรายละเอียดเกี่ยวกับการเปรียบเทียบได้มากนัก แต่แรงจูงใจทั้งหมดที่อยู่เบื้องหลังการนำไปใช้ด้วยวิธีนี้ IMO คือการหลีกเลี่ยงการเปลี่ยนแปลงที่สำคัญในการนำ JVM มาใช้
Rohit Jain

1
ทำไมจะ "ไม่ทำงานแน่นอน" อย่างที่คุณบอกว่าไม่มีข้อมูลประเภท (ทั่วไป) ดังนั้นไม่มีอะไรให้รันไทม์เพื่อตรวจสอบ มันอาจจะล้มเหลวในขณะรันไทม์หากมีการป้อนประเภทที่ไม่ถูกต้อง แต่ไม่มี "ความแน่นอน" เกี่ยวกับสิ่งนั้น
Hot Licks

1
@RohitJain: ผมไม่ได้วิจารณ์แนวคิดทั่วไปของ Java แต่ผลเดียวที่ยังคงสร้างรหัสน่าเกลียด ;-)
DR

1
@DR - Java generics น่าเกลียดจาก git-go ส่วนใหญ่เป็นเพียงแค่คุณสมบัติ C ++
Hot Licks
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.