2016-06-24 20 views
8

Mặc dù it is possible to serialize a lambda in Java 8, nó là strongly discouraged; thậm chí serializing inner classes is discouraged. Lý do được đưa ra là lambdas có thể không deserialize đúng trên một JRE. Tuy nhiên, điều này không có nghĩa là có một cách để tuần tự an toàn một lambda?Làm thế nào để tuần tự an toàn một lambda?

Ví dụ, nói tôi xác định một lớp học để có một cái gì đó như thế này:

public class MyClass { 
    private String value; 
    private Predicate<String> validateValue; 

    public MyClass(String value, Predicate<String> validate) { 
     this.value = value; 
     this.validateValue = validate; 
    } 

    public void setValue(String value) { 
     if (!validateValue(value)) throw new IllegalArgumentException(); 
     this.value = value; 
    } 

    public void setValidation(Predicate<String> validate) { 
     this.validateValue = validate; 
    } 
} 

Nếu tôi tuyên bố một thể hiện của lớp như thế này, tôi không nên serialize nó:

MyClass obj = new MyClass("some value", (s) -> !s.isEmpty()); 

Nhưng nếu tôi tạo một thể hiện của lớp như thế này:

// Could even be a static nested class 
public class IsNonEmpty implements Predicate<String>, Serializable { 
    @Override 
    public boolean test(String s) { 
     return !s.isEmpty(); 
    } 
} 
MyClass isThisSafeToSerialize = new MyClass("some string", new IsNonEmpty()); 

Điều này có an toàn để sắp xếp theo thứ tự không? Bản năng của tôi nói rằng có, nó phải được an toàn, vì không có lý do gì mà các giao diện trong java.util.function nên được xử lý bất kỳ cách nào khác với bất kỳ giao diện ngẫu nhiên nào khác. Nhưng tôi vẫn cảnh giác.

+1

Giao diện hoàn toàn không liên quan đến việc Serialization, do đó, có, triển khai 'Predicate' có tác động tương tự như triển khai bất kỳ giao diện nào khác, * none *. Nhưng giả định của bạn rằng lambdas có thể không deserialize đúng trên một JRE là sai.Có một [đại diện liên tục được xác định rõ] (https://docs.oracle.com/javase/8/docs/api/?java/lang/invoke/SerializedLambda.html). – Holger

+0

@Holger Vậy tại sao [tài liệu oracle] (https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#serialization) dường như gợi ý rằng chúng không? – Justin

+1

Vâng, nó chứa một tham chiếu đến [vấn đề liên quan đến lớp bên trong với serialization] (https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html#serialization). Tóm lại, nó có khả năng tạo ra các phụ thuộc trình biên dịch, chứ không phải các vấn đề cụ thể của JRE. Cấp, văn bản là có một chút gây hiểu lầm. Và quan tâm đến nguy cơ vô tình tuần tự hóa các giá trị được chụp của bối cảnh xung quanh, bao gồm 'this'… – Holger

Trả lời

9

Tùy thuộc vào loại an toàn bạn muốn. Đó không phải là trường hợp lambdas được tuần tự hóa không thể được chia sẻ giữa các JRE khác nhau. Họ có một đại diện liên tục được xác định rõ ràng, SerializedLambda. Khi bạn nghiên cứu, nó hoạt động như thế nào, bạn sẽ thấy rằng nó dựa vào sự hiện diện của lớp xác định, nó sẽ có một phương pháp đặc biệt để xây dựng lại lambda.

Điều gì làm cho nó không đáng tin cậy là sự phụ thuộc vào các tạo tác trình biên dịch cụ thể, ví dụ: phương pháp đích tổng hợp, có một số tên được tạo ra, do đó, các thay đổi đơn giản như chèn một biểu thức lambda khác hoặc biên dịch lại lớp với trình biên dịch khác có thể phá vỡ tính tương thích với biểu thức lambda tuần tự hiện có.

Tuy nhiên, việc sử dụng các lớp được viết theo cách thủ công không miễn nhiễm với điều này. Nếu không có khai báo rõ ràng serialVersionUID, thuật toán mặc định sẽ tính toán một id bằng các tạo phẩm lớp băm, bao gồm private và các thành phần tổng hợp, thêm một sự phụ thuộc trình biên dịch tương tự. Vì vậy, tối thiểu để làm, nếu bạn muốn các biểu mẫu bền vững đáng tin cậy, là tuyên bố rõ ràng serialVersionUID.

Hoặc bạn chuyển sang các hình thức mạnh mẽ nhất có thể:

public enum IsNonEmpty implements Predicate<String> { 
    INSTANCE; 

    @Override 
    public boolean test(String s) { 
     return !s.isEmpty(); 
    } 
} 

Serializing liên tục này không lưu trữ bất kỳ tính chất của việc thực hiện thực tế, bên cạnh tên lớp của nó (và thực tế là nó là một enum, tất nhiên) và tham chiếu đến tên của hằng số. Khi deserialization, trường hợp duy nhất thực tế của tên đó sẽ được sử dụng.


Lưu ý rằng serializable lambda expressions may create security issues vì chúng mở một cách thay thế bằng tay trên đối tượng cho phép gọi phương pháp đích. Tuy nhiên, điều này áp dụng cho tất cả các lớp serializable, như tất cả các biến thể được hiển thị trong câu hỏi của bạn và câu trả lời này cho phép cố ý deserialize một đối tượng cho phép để gọi các hoạt động đóng gói. Nhưng với các lớp có thể tuần tự rõ ràng, tác giả thường nhận thức rõ hơn về thực tế này.

+0

Tôi không thấy bất kỳ nguy hiểm nào với enum; khi được tuần tự hóa, về cơ bản nó chỉ là tên lớp và tên cá thể. Làm thế nào là một vấn đề an ninh? Có phải vấn đề cơ bản là kẻ tấn công có thể truy cập vào 'INSTANCE' khi tôi không dự định điều đó xảy ra? – Justin

+2

Chính xác. Việc tạo một lớp tuần tự hóa giống như thêm một hàm tạo 'public' bổ sung (hoặc accessor), có thể được sử dụng ngay cả khi chính lớp đó không phải là' public'. kết hợp với một giao diện chung như 'Predicate', nó ngụ ý cung cấp quyền truy cập vào hoạt động đóng gói. Nếu bản thân hoạt động không quan trọng, thì không có vấn đề gì. – Holger

Các vấn đề liên quan