Làm thế nào để nối tiếp một lambda?


157

Làm thế nào tôi có thể nối tiếp thanh lịch một lambda?

Ví dụ, mã dưới đây ném a NotSerializableException. Làm thế nào tôi có thể sửa nó mà không tạo SerializableRunnablegiao diện "giả"?

public static void main(String[] args) throws Exception {
    File file = Files.createTempFile("lambda", "ser").toFile();
    try (ObjectOutput oo = new ObjectOutputStream(new FileOutputStream(file))) {
        Runnable r = () -> System.out.println("Can I be serialized?");
        oo.writeObject(r);
    }

    try (ObjectInput oi = new ObjectInputStream(new FileInputStream(file))) {
        Runnable  r = (Runnable) oi.readObject();
        r.run();
    }
}

18
Trong khi điều này là có thể (xem câu trả lời đã chọn), mọi người có lẽ nên suy nghĩ kỹ về việc thực sự làm điều này. Nó chính thức "không được khuyến khích" và có thể có ý nghĩa bảo mật nghiêm trọng .
David

Câu trả lời:


260

Java 8 giới thiệu khả năng truyền một đối tượng đến một giao điểm của các loại bằng cách thêm nhiều giới hạn . Trong trường hợp tuần tự hóa, do đó có thể viết:

Runnable r = (Runnable & Serializable)() -> System.out.println("Serializable!");

Và lambda tự động trở thành tuần tự.


4
Rất thú vị - tính năng này có vẻ khá mạnh mẽ. Có bất kỳ việc sử dụng một biểu hiện như vậy bên ngoài đúc lambdas? Ví dụ, bây giờ cũng có thể làm một cái gì đó tương tự với một lớp ẩn danh thông thường?
Balder

6
@Balder Cơ sở để chuyển sang loại giao lộ đã được thêm vào để cung cấp loại mục tiêu cho loại suy luận của lambdas. Vì AIC có một loại bảng kê khai (nghĩa là loại của nó không được suy ra) nên việc đưa AIC sang loại giao cắt không hữu ích. (Có thể, chỉ là không hữu ích.) Để AIC triển khai nhiều giao diện, bạn phải tạo một giao diện con mới mở rộng tất cả chúng, và sau đó khởi tạo điều đó.
Stuart Marks

2
Điều này sẽ tạo ra một cảnh báo trình biên dịch, nói rằng không có serialVersionUID được xác định?
Kirill Rakhman

11
Lưu ý: điều này chỉ hoạt động nếu bạn áp dụng các diễn viên trong quá trình xây dựng. Sau đây sẽ đưa ra ClassCastException: Runnable r = () -> System.out.println ("Nối tiếp!"); Runnable serializableR = (Runnable & serializable) r;
bcody


24

Việc xây dựng tương tự có thể được sử dụng để tham khảo phương pháp. Ví dụ mã này:

import java.io.Serializable;

public class Test {
    static Object bar(String s) {
        return "make serializable";
    }

    void m () {
        SAM s1 = (SAM & Serializable) Test::bar;
        SAM s2 = (SAM & Serializable) t -> "make serializable";
    }

    interface SAM {
        Object action(String s);
    }
}

định nghĩa biểu thức lambda và tham chiếu phương thức với kiểu mục tiêu tuần tự hóa.


18

Diễn viên rất xấu xí. Tôi thích xác định một phần mở rộng nối tiếp cho giao diện chức năng tôi đang sử dụng

Ví dụ:

interface SerializableFunction<T,R> extends Function<T,R>, Serializable {}
interface SerializableConsumer<T> extends Consumer<T>, Serializable {}

sau đó phương thức chấp nhận lambda có thể được định nghĩa như sau:

private void someFunction(SerializableFunction<String, Object> function) {
   ...
}

và gọi hàm bạn có thể vượt qua lambda của bạn mà không có bất kỳ diễn viên xấu xí nào:

someFunction(arg -> doXYZ(arg));

2
Tôi thích câu trả lời này bởi vì sau đó bất kỳ người gọi bên ngoài nào bạn không viết cũng sẽ tự động được nối tiếp. Nếu bạn muốn các đối tượng được gửi thành tuần tự hóa, giao diện của bạn phải được tuần tự hóa, đó là loại điểm của giao diện. Tuy nhiên, câu hỏi đã nói "không tạo SerializableRunnablegiao diện 'hình nộm"
slevin

4

Trong trường hợp ai đó rơi vào đây trong khi tạo mã Beam / Dataflow:

Chùm có riêng của mình SerializableFunction Interface vì vậy không cần cho giao diện giả hoặc tiết phôi.


3

Nếu bạn sẵn sàng chuyển sang một khung công tác tuần tự hóa khác như Kryo , bạn có thể thoát khỏi nhiều giới hạn hoặc yêu cầu mà giao diện đã triển khai phải thực hiện Serializable. Cách tiếp cận là

  1. Sửa đổi InnerClassLambdaMetafactoryđể luôn tạo mã cần thiết cho tuần tự hóa
  2. Gọi trực tiếp LambdaMetaFactorytrong quá trình khử lưu huỳnh

Để biết chi tiết và mã xem bài đăng blog này

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.