Đọc về thông số kỹ thuật Java-8, tôi tiếp tục thấy các tài liệu tham khảo về 'các loại SAM'. Tôi đã không thể tìm thấy một lời giải thích rõ ràng về điều này là gì.
Loại SAM là gì và kịch bản ví dụ khi nào có thể được sử dụng?
Đọc về thông số kỹ thuật Java-8, tôi tiếp tục thấy các tài liệu tham khảo về 'các loại SAM'. Tôi đã không thể tìm thấy một lời giải thích rõ ràng về điều này là gì.
Loại SAM là gì và kịch bản ví dụ khi nào có thể được sử dụng?
Câu trả lời:
Để tóm tắt liên kết Jon posted 1 trong trường hợp nó bao giờ đi xuống, "SAM" là viết tắt của "phương pháp trừu tượng đơn lẻ", và "SAM-type" đề cập đến giao diện như Runnable
, Callable
vv biểu thức Lambda, một tính năng mới trong Java 8, là được coi là một loại SAM và có thể được tự do chuyển đổi thành chúng.
Ví dụ: với giao diện như thế này:
public interface Callable<T> {
public T call();
}
Bạn có thể khai báo một Callable
biểu thức lambda như thế này:
Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"
Biểu thức Lambda trong bối cảnh này chủ yếu chỉ là đường cú pháp. Chúng trông mã tốt hơn các lớp ẩn danh và ít hạn chế hơn trong việc đặt tên phương thức. Lấy ví dụ này từ liên kết:
class Person {
private final String name;
private final int age;
public static int compareByAge(Person a, Person b) { ... }
public static int compareByName(Person a, Person b) { ... }
}
Person[] people = ...
Arrays.sort(people, Person::compareByAge);
Điều này tạo ra một Comparator
cách sử dụng một phương thức cụ thể không chia sẻ cùng tên Comparator.compare
, theo cách đó bạn không phải tuân theo cách đặt tên giao diện của phương thức và bạn có thể có nhiều ghi đè so sánh trong một lớp, sau đó tạo các trình so sánh nhanh chóng các biểu thức lambda.
Đi sâu hơn...
Ở mức độ sâu hơn, Java thực hiện những điều này bằng cách sử dụng lệnh invokedynamic
bytecode được thêm vào trong Java 7. Tôi đã nói trước đó rằng việc khai báo Lambda tạo ra một thể hiện Callable
hoặc Comparable
tương tự như một lớp ẩn danh, nhưng điều đó không hoàn toàn đúng. Thay vào đó, lần đầu tiên invokedynamic
được gọi, nó tạo ra một trình xử lý hàm Lambda bằng LambdaMetafactory.metafactory
phương thức này , sau đó sử dụng thể hiện được lưu trong bộ nhớ cache này trong các lệnh gọi tương lai của Lambda. Thông tin thêm có thể được tìm thấy trong câu trả lời này .
Cách tiếp cận này rất phức tạp và thậm chí bao gồm mã có thể đọc các giá trị nguyên thủy và tham chiếu trực tiếp từ bộ nhớ ngăn xếp để chuyển vào mã Lambda của bạn (ví dụ để đi xung quanh cần phân bổ một Object[]
mảng để gọi Lambda của bạn), nhưng nó cho phép lặp lại quá trình thực hiện Lambda trong tương lai để thay thế các triển khai cũ mà không phải lo lắng về khả năng tương thích mã byte. Nếu các kỹ sư tại Oracle thay đổi triển khai Lambda cơ bản trong phiên bản JVM mới hơn, Lambdas được biên dịch trên JVM cũ hơn sẽ tự động sử dụng triển khai mới hơn mà không có bất kỳ thay đổi nào về phía nhà phát triển.
1 Cú pháp trên liên kết đã hết hạn. Hãy xem Lambda Expressions Java Trail để xem cú pháp hiện tại.