Từ Oracle method references tutorial: Reference
đến một phương pháp dụ của một đối tượng tùy ý của một loại đặc biệt
Sau đây là một ví dụ về một tham chiếu đến một phương pháp thể hiện của một đối tượng tùy ý một loại đặc biệt:
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
Biểu thức lambda tương đương cho tham chiếu phương thức String::compareToIgnoreCase
sẽ có danh sách tham số chính thức (String a, String b)
, trong đó a và b là các tên tùy ý được sử dụng để mô tả tốt hơn ví dụ này. Tham chiếu phương thức sẽ gọi phương thức a.compareToIgnoreCase(b)
.
Nhưng, những gì các nhà điều hành ::
thực sự nghĩa là gì? Vâng, các nhà điều hành ::
thể được mô tả như thế này (từ this SO question):
Phương pháp tham khảo có thể thu được trong phong cách khác nhau, nhưng tất cả đều có nghĩa như nhau:
- Một phương pháp tĩnh (
ClassName::methodName
)
- một phương pháp thể hiện của một đối tượng cụ thể (
instanceRef::methodName
)
- một phương pháp siêu của một đối tượng cụ thể (
super::methodName
)
- Một phương pháp thể hiện của một đối tượng tùy tiện của một loại đặc biệt (
ClassName::methodName
)
- Tham chiếu constructor lớp (
ClassName::new
)
- Một tài liệu tham khảo constructor mảng (
TypeName[]::new
)
Vì vậy, đó có nghĩa là phương pháp tham chiếu String::compareToIgnoreCase
thuộc danh mục thứ hai (instanceRef::methodName
) có nghĩa là nó có thể được dịch sang (a, b) -> a.compareToIgnoreCase(b)
.
Tôi tin rằng các ví dụ sau minh họa điều này hơn nữa. A Comparator<String>
chứa một phương thức hoạt động trên hai toán hạng String
và trả về một int
. Điều đó có thể được mô tả giả là (a, b) ==> return int
(trong đó các toán hạng là a
và b
). Nếu bạn xem theo cách đó, tất cả các mục sau thuộc danh mục đó:
// Trad anonymous inner class
// Operands: o1 and o2. Return value: int
Comparator<String> cTrad = new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
return o1.compareToIgnoreCase(o2);
}
};
// Lambda-style
// Operands: o1 and o2. Return value: int
Comparator<String> cLambda = (o1, o2) -> o1.compareToIgnoreCase(o2);
// Method-reference à la bullet #2 above.
// The invokation can be translated to the two operands and the return value of type int.
// The first operand is the string instance, the second operand is the method-parameter to
// to the method compareToIgnoreCase and the return value is obviously an int. This means that it
// can be translated to "instanceRef::methodName".
Comparator<String> cMethodRef = String::compareToIgnoreCase;
This great SO-answer to explains how lambda functions are compiled. Trong câu trả lời đó, Jarandinor đề cập đến đoạn sau đây từ tài liệu tuyệt vời của Brian Goetz mô tả more about lambda translations.
Thay vì tạo bytecode để tạo đối tượng triển khai biểu thức lambda (như gọi hàm tạo cho lớp bên trong), chúng tôi mô tả công thức xây dựng lambda và ủy quyền xây dựng thực tế cho thời gian chạy ngôn ngữ. Công thức đó được mã hóa trong các danh sách đối số tĩnh và động của một lệnh gọi động.
Về cơ bản, điều này có nghĩa là thời gian chạy gốc quyết định cách dịch lambda.
Brian tiếp tục:
Phương pháp tài liệu tham khảo được đối xử giống như biểu thức lambda, ngoại trừ rằng hầu hết tài liệu tham khảo phương pháp không cần phải được khử đường vào một phương pháp mới; chúng ta có thể chỉ cần tải một phương thức xử lý hằng số cho phương thức được tham chiếu và chuyển nó tới metafactory.
Vì vậy, lambdas là khử đường thành một phương pháp mới. Ví dụ.
class A {
public void foo() {
List<String> list = ...
list.forEach(s -> { System.out.println(s); });
}
}
Đoạn mã trên sẽ khử đường một cái gì đó như thế này:
class A {
public void foo() {
List<String> list = ...
list.forEach([lambda for lambda$1 as Consumer]);
}
static void lambda$1(String s) {
System.out.println(s);
}
}
Nhưng, Brian cũng giải thích điều này trong tài liệu:
nếu phương pháp khử đường là một ví dụ phương thức, người nhận được coi là đối số đầu tiên
Brian tiếp tục giải thích rằng đối số còn lại của lambda được chuyển làm đối số cho phương thức được giới thiệu.
Như vậy, với sự giúp đỡ của this entry by Moandji Ezana, các desugaring của compareToIgnoreCase
như một Comparator<String>
có thể được chia nhỏ các bước sau:
Collections#sort
cho một List<String>
hy vọng một Comparator<String>
Comparator<String>
là một giao diện chức năng với phương pháp int sort(String, String)
, tương đương với BiFunction<String, String, Integer>
- Trường hợp so sánh do đó có thể được cung cấp bởi
BiFunction
lambda -tương thích: (String a, String b) -> a.compareToIgnoreCase(b)
String::compareToIgnoreCase
đề cập đến một phương pháp dụ rằng có một cuộc tranh luận String
, vì vậy nó là tương thích với lambda trên: String a
trở thành người nhận và String b
trở thành lý luận phương pháp
Edit: sau khi đầu vào từ OP tôi đã thêm một mức thấp dụ giải thích Thú vị câu hỏi desugaring
, thậm chí có thể ví dụ rõ ràng hơn: 'Comparator super String> cmp = String :: compareToIgnoreCase; 'Thành thực mà nói, tôi không biết nó hoạt động như thế nào. – MightyPork
Tôi tin rằng điều này có thể bị đóng dưới dạng bản sao của http://stackoverflow.com/questions/20001427/double-colon-operator-in-java-8 – MightyPork