2015-06-26 20 views
12

Tôi có một câu hỏi liên quan đến Java 8. Đây là mã nguồn của tôi:Áp dụng JDK 8 Consumer trên chuỗi

final Consumer<String> d = e -> System.out.println(e); 
final Function<String, String> upper = x -> x.toUpperCase(); 
final Function<String, String> lower = x -> x.toLowerCase(); 

new Thread(() -> d.accept(upper.apply("hello 1"))).run(); 
new Thread(() -> d.accept(lower.apply("hello 2"))).run(); 

này hoạt động khá tốt và tạo ra kết quả như sau:

HELLO 1 
hello 2 

câu hỏi của tôi bây giờ là nếu cú ​​pháp trên d.acceptupper.apply là chỉ có thể một hoặc nếu có thêm một số kiểu "java 8 lambda" chúng ta có thể viết hai dòng cuối cùng.

+1

Các lambdas này là tất cả các ứng cử viên lý tưởng để sử dụng _method references_ thay thế: System.out :: println, String :: toUpperCase. –

Trả lời

9

Trước khi nói bất cứ điều gì về về các biểu thức lambda hoặc giao diện chức năng, chúng ta phải nói về sai lầm thực sự có vấn đề của bạn: bạn đang gọi sốrun()trên một chuỗi! Nếu bạn muốn bắt đầu một chủ đề mới, bạn phải gọi start() trên cá thể Thread, nếu bạn muốn chạy mã tuần tự, không tạo ra một Thread (nhưng chỉ một Runnable).

Điều đó nói rằng, có một số phương pháp default trên các giao diện chức năng của Java 8 để kết hợp các hàm, ví dụ: bạn có thể chuỗi hai số Function s qua số Function.andThen(…) nhưng các kết hợp có sẵn sẽ không được hoàn thành.

Nếu một nhiệm vụ kết hợp nào đó lặp đi lặp lại trong ứng dụng của bạn, bạn có thể xem xét việc tạo phương pháp hữu ích:

public static <T> Runnable bind(T value, Consumer<T> c) { 
    return()->c.accept(value); 
} 
public static <T,U> Consumer<U> compose(Function<U,T> f, Consumer<? super T> c) { 
    return u->c.accept(f.apply(u)); 
} 

new Thread(bind("Hello 1", compose(upper, d))).start(); 
new Thread(bind("Hello 2", compose(lower, d))).start(); 

Nhưng ba phần này trông giống như một nhiệm vụ cho các API dòng:

Stream.of("Hello 1").map(upper).forEach(d); 
Stream.of("Hello 2").map(lower).forEach(d); 

Tôi đã tạo chuỗi mới ở đây vì không có bất kỳ lợi ích nào.

Nếu bạn thực sự muốn xử lý song song, bạn có thể làm điều đó trên một cơ sở cho mỗi nhân vật:

"Hello 1".chars().parallel() 
    .map(Character::toUpperCase).forEachOrdered(c->System.out.print((char)c)); 

nhưng vẫn sẽ không có bất kỳ lợi ích cho sự đơn giản của các nhiệm vụ và các chi phí cố định của tiến trình song song.

+1

Về việc xử lý song song: thực thi 'chars()' mặc định trong JDK8 thực sự là xấu. Không chắc bạn sẽ nhận được tốc độ đáng chú ý ngay cả đối với các chuỗi dài. Nó tốt hơn nhiều trong JDK9. –

+2

@Tagir Valeev: Tôi biết điều đó nhưng tôi không muốn đi sâu vào chi tiết.Xét cho cùng, ngay cả với sự hỗ trợ chia tách tốt hơn của Java 9, nhiệm vụ của câu hỏi này sẽ không được hưởng lợi từ việc thực thi song song. Tôi nghi ngờ rằng có thể có một 'String' đủ dài để một phép biến đổi' toUpperCase' đơn giản có thể được hưởng lợi từ việc thực thi song song. Bằng cách này, API Java 8 có một cách để vượt qua giới hạn này: sử dụng 'CharBuffer.wrap (string) .chars()' thay vì 'string.chars()'… – Holger

+0

Cảm ơn bạn rất nhiều. Điều này đã giúp tôi rất nhiều. Bạn đang hoàn toàn đúng với Thread.run. Đây chỉ là một ví dụ và đã liên quan đến lambdas. –

3

Vâng, đó là cú pháp duy nhất có thể. Trên thực tế khi bạn sử dụng lambda bạn thậm chí không biết liệu nó thực sự lambda hay chỉ là một thực hiện đồng bằng cũ của giao diện nhất định (thông qua lớp vô danh hoặc thậm chí cả lớp bình thường). Vì vậy, bạn phải sử dụng giao diện chức năng như bất kỳ giao diện Java nào khác: gọi phương thức của nó một cách rõ ràng.

4

Bạn cũng có thể viết như thế này:

new Thread(() -> Stream.of("hello 1").map(upper).forEach(d)).run(); 
    new Thread(() -> Stream.of("hello 1").map(lower).forEach(d)).run(); 

Hoặc directlry hơn:

new Thread(() -> Stream.of("hello 1").map(String::toUpperCase).forEach(System.out::println)).run(); 
    new Thread(() -> Stream.of("hello 1").map(String::toLowerCase).forEach(System.out::println)).run(); 
Các vấn đề liên quan