"Loại SAM" trong Java là gì?


133

Đọ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?


4
Xem cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (mà tôi tìm thấy sau một tìm kiếm duy nhất, đưa tôi đến một câu hỏi SO khác).
Jon Skeet

2
Vui lòng không giới thiệu mọi người đến thông tin lỗi thời. Một tìm kiếm dài hơn một chút sẽ đưa bạn đến phiên bản cập nhật hơn: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , bản thân nó đã 18 tháng tuổi và hiện đã lỗi thời nơi. Câu hỏi của OP được trả lời tại lambdafaq.org/what-is-a-feftal-interface , một trang trong Câu hỏi thường gặp mà tôi cố gắng cập nhật về những gì gần đây đã phát triển ngôn ngữ và API thay đổi nhanh chóng.
Maurice Naftalin

1
@MauriceNaftalin Bạn cũng có thể liên kết đường mòn Java , được nhóm phát triển cập nhật.
Brian

1
Thuật ngữ hiện tại là "giao diện chức năng".
newacct

1
@MauriceNaftalin: Xin lỗi, đã bỏ lỡ cái đó. Chắc chắn sẽ liên kết với nó nếu tôi tìm thấy điều đó.
Jon Skeet

Câu trả lời:


142

Để 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, Callablevv 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 Callablebiể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 invokedynamicbytecode đượ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 Callablehoặc Comparabletươ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.metafactoryphươ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.

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.