Có thể truyền Luồng trong Java 8 không?


160

Có thể truyền một luồng trong Java 8 không? Nói rằng tôi có một danh sách các đối tượng, tôi có thể làm một cái gì đó như thế này để lọc ra tất cả các đối tượng bổ sung:

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

Sau này, nếu tôi muốn làm gì đó với khách hàng, tôi sẽ cần phải chọn từng người trong số họ:

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

Cái này trông hơi xấu xí. Có thể truyền toàn bộ một luồng sang một loại khác không? Thích cast Stream<Object>đến a Stream<Client>?

Hãy bỏ qua thực tế rằng làm những việc như thế này có thể có nghĩa là thiết kế xấu. Chúng tôi làm những thứ như thế này trong lớp khoa học máy tính của tôi, vì vậy tôi đã xem xét các tính năng mới của java 8 và tò mò liệu điều này có thể không.


3
Từ quan điểm của thời gian chạy Java, hai loại Luồng đã giống nhau, do đó không cần truyền. Bí quyết là lén nó qua trình biên dịch. (Đó là, giả sử nó có ý nghĩa để làm như vậy.)
Hot Licks

Câu trả lời:


283

Tôi không nghĩ có một cách để làm điều đó. Một giải pháp có thể sạch hơn sẽ là:

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

hoặc, như được đề xuất trong các nhận xét, bạn có thể sử dụng castphương pháp - mặc dù trước đây có thể dễ đọc hơn:

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

Đây là khá nhiều những gì tôi đang tìm kiếm. Tôi đoán rằng tôi đã bỏ qua việc truyền nó cho Khách hàng mapsẽ trả lại a Stream<Client>. Cảm ơn!
Phiên bản

+1 cách mới thú vị, mặc dù chúng có nguy cơ nhận được mã spaghetti thuộc loại thế hệ mới (ngang, không dọc)
robermann 20/03/2016

@LordOfThePigs Có, nó hoạt động mặc dù tôi không chắc liệu mã có rõ ràng hơn không. Tôi đã thêm ý tưởng vào câu trả lời của tôi.
assylias

38
Bạn có thể "đơn giản hóa" bộ lọc instanceOf bằng:Stream.of(objects).filter(Client.class::isInstance).[...]
Nicolas Labrot

Phần không có mũi tên thực sự rất đẹp <3
Fabich

14

Dọc theo dòng câu trả lời của ggovan , tôi làm điều này như sau:

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

Sử dụng chức năng trợ giúp này:

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

10

Đến bữa tiệc muộn, nhưng tôi nghĩ đó là một câu trả lời hữu ích.

flatMap sẽ là cách ngắn nhất để làm điều đó.

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

Nếu olà một Clientthì tạo một luồng với một phần tử, nếu không thì sử dụng luồng trống. Những luồng này sau đó sẽ được làm phẳng thành a Stream<Client>.


Tôi đã cố gắng thực hiện điều này, nhưng nhận được một cảnh báo nói rằng lớp của tôi "sử dụng các hoạt động không được kiểm soát hoặc không an toàn" - điều đó có được mong đợi không?
aweibell

không may là đúng vậy. Nếu bạn sử dụng một if/elsethay vì ?:toán tử thì sẽ không có cảnh báo. Hãy yên tâm bạn có thể an toàn cảnh báo.
ggovan

3
Trên thực tế điều này dài hơn Stream.of(objects).filter(o->o instanceof Client).map(o -> (Client)o)hoặc thậm chí Stream.of(objects).filter(Client.class::isInstance).map(Client.class::cast).
Didier L

4

Cái này trông hơi xấu xí. Có thể truyền toàn bộ một luồng sang một loại khác không? Thích cast Stream<Object>đến a Stream<Client>?

Không, điều đó là không thể. Điều này không mới trong Java 8. Điều này đặc trưng cho khái quát. A List<Object>không phải là một loại siêu List<String>, vì vậy bạn không thể List<Object>chuyển sang a List<String>.

Tương tự là vấn đề ở đây. Bạn không thể cast Stream<Object>để Stream<Client>. Tất nhiên bạn có thể sử dụng nó một cách gián tiếp như thế này:

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

nhưng điều đó không an toàn và có thể thất bại khi chạy. Lý do cơ bản cho điều này là, thuốc generic trong Java được triển khai bằng cách sử dụng xóa. Vì vậy, không có thông tin loại có sẵn về loại của Streamnó trong thời gian chạy. Mọi thứ chỉ là Stream.

BTW, có gì sai với cách tiếp cận của bạn? Trông ổn với tôi.


2
@DR Generics in C#được triển khai bằng cách sử dụng, trong khi ở Java, nó được triển khai bằng cách xóa. Cả hai đều được thực hiện trong thời trang khác nhau cơ bản. Vì vậy, bạn không thể mong đợi nó hoạt động theo cùng một cách trong cả hai ngôn ngữ.
Rohit Jain

1
@DR Tôi hiểu rằng việc tẩy xóa đặt ra nhiều vấn đề cho người mới bắt đầu hiểu khái niệm về khái quát trong Java. Và vì tôi không sử dụng C #, tôi không thể đi sâu vào chi tiết so sánh. Nhưng toàn bộ động lực đằng sau việc thực hiện nó theo cách này IMO là để tránh những thay đổi lớn trong việc triển khai JVM.
Rohit Jain

1
Tại sao nó "chắc chắn thất bại trong thời gian chạy"? Như bạn nói không có thông tin loại (chung chung), vì vậy không có gì để kiểm tra thời gian chạy. Nó có thể có thể thất bại trong thời gian chạy, nếu các loại sai được đưa qua, nhưng không có "sự chắc chắn" nào về điều đó.
Hot Licks

1
@RohitJain: Tôi không chỉ trích khái niệm chung của Java, nhưng hậu quả duy nhất này vẫn tạo ra mã xấu ;-)
DR

1
@DR - Java generic là xấu xí từ git-go. Chủ yếu chỉ là tính năng C ++ ham muốn.
Licks nóng
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.