Lambda Biểu thức và phương pháp chung


111

Giả sử tôi có một giao diện chung:

interface MyComparable<T extends Comparable<T>>  {
    public int compare(T obj1, T obj2);
}

Và một phương pháp sort:

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable<T> comp) {
    // sort the list
}

Tôi có thể gọi phương thức này và chuyển một biểu thức lambda làm đối số:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

Điều đó sẽ hoạt động tốt.

Nhưng bây giờ nếu tôi làm cho giao diện không chung chung và phương pháp chung chung:

interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2);
}

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable comp) {
}

Và sau đó gọi điều này như:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

Nó không biên dịch. Nó hiển thị lỗi ở biểu thức lambda nói:

"Phương pháp mục tiêu là chung"

OK, khi tôi biên dịch nó bằng cách sử dụng javac, nó hiển thị lỗi sau:

SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
        sort(list, (a, b) -> a.compareTo(b));
            ^
    (argument mismatch; invalid functional descriptor for lambda expression
      method <T#2>(T#2,T#2)int in interface MyComparable is generic)
  where T#1,T#2 are type-variables:
    T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
    T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error

Từ thông báo lỗi này, có vẻ như trình biên dịch không thể suy ra các đối số kiểu. Đó là trường hợp? Nếu có, thì tại sao nó lại xảy ra như thế này?

Tôi đã thử nhiều cách khác nhau, tìm kiếm trên internet. Sau đó, tôi tìm thấy bài viết JavaCodeGeeks này , trong đó chỉ ra một cách, vì vậy tôi đã thử:

sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));

mà một lần nữa không hoạt động, trái với những gì bài báo đó tuyên bố rằng nó hoạt động. Có thể nó đã từng hoạt động trong một số bản dựng ban đầu.

Vì vậy, câu hỏi của tôi là: Có cách nào để tạo biểu thức lambda cho một phương thức chung không? Tuy nhiên, tôi có thể thực hiện việc này bằng cách sử dụng tham chiếu phương thức, bằng cách tạo một phương thức:

public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
    return obj1.compareTo(obj2);
}

trong một số lớp nói SO, và chuyển nó dưới dạng:

sort(list, SO::compare);

Câu trả lời:


117

Bạn không thể sử dụng biểu thức lambda cho một giao diện chức năng , nếu phương thức trong giao diện chức năngtham số kiểu . Xem phần §15.27.3 trong JLS8 :

Biểu thức lambda tương thích [..] với kiểu đích T nếu T là kiểu giao diện chức năng (§9.8) và biểu thức là đồng dư với kiểu hàm của [..] T. [..] Biểu thức lambda là đồng dư với một loại hàm nếu tất cả những điều sau đây đều đúng:

  • Kiểu hàm không có tham số kiểu .
  • [..]

47
Tuy nhiên, hạn chế này không áp dụng cho các tham chiếu phương pháp đến các phương pháp chung. Bạn có thể sử dụng tham chiếu phương pháp cho một phương pháp chung với giao diện chức năng chung.
Brian Goetz

17
Tôi chắc rằng có lý do chính đáng cho sự hạn chế này. Nó là gì?
Sandro

6
@Sandro: đơn giản là không có cú pháp nào để khai báo các tham số kiểu cho một biểu thức lambda. Và một cú pháp như vậy sẽ rất phức tạp. Hãy nhớ rằng trình phân tích cú pháp vẫn phải có khả năng cho một biểu thức lambda như vậy với các tham số kiểu ngoài các cấu trúc Java hợp pháp khác. Vì vậy, bạn phải sử dụng tham chiếu phương pháp. Phương thức đích có thể khai báo các tham số kiểu bằng cú pháp đã thiết lập.
Holger

2
@Holger vẫn còn, trong đó các tham số kiểu có thể được tự động suy luận, trình biên dịch có thể giải mã kiểu capure như khi bạn khai báo, ví dụ: Set <?> Và thực hiện kiểm tra kiểu với các kiểu đã bắt đó. Chắc chắn, điều đó khiến bạn không thể cung cấp chúng dưới dạng tham số kiểu trong nội dung, nhưng nếu bạn cần, sử dụng đến tham chiếu phương thức là một lựa chọn thay thế tốt
WorldSEnder 14/02/17

17

Sử dụng tham chiếu phương thức, tôi đã tìm thấy cách khác để truyền đối số:

List<String> list = Arrays.asList("a", "b", "c");        
sort(list, Comparable::<String>compareTo);

3

Trình biên dịch chỉ trỏ phiên bản phù hợp của Trình so sánh chung với (Comparator<String>)

Vì vậy, câu trả lời sẽ là

sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));


2
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparableMyComparablekhông phải là chung chung (không loại) nên (MyComparable<String>)sẽ không làm việc, hoặc
user85421

1
Không biết bạn đang gõ mã @CarlosHeuberger như thế nào, nhưng nó hoạt động rất tốt đối với tôi, đây là những gì tôi đang tìm kiếm.
Ivan Perales M.

@IvanPeralesM. sau 2 tháng ... Tôi đã sao chép & dán mã của bạn và sao chép & dán dòng phía trên lên dòng sắp xếp của bạn - chỉ thế thôi, như ở đây: Ideone.com/YNwBbF ! Bạn có chắc là bạn đã nhập chính xác mã trên không? sử dụng Compartor?
user85421

Không, tôi không, tôi sử dụng ý tưởng đằng sau câu trả lời, để truyền hàm để cho trình biên dịch biết nó là kiểu gì và nó hoạt động.
Ivan Perales M.

@IvanPeralesM. Vậy thì vấn đề của bạn với "đánh máy" của tôi là gì? Câu trả lời, như nó được đăng, không hoạt động.
user85421

0

Ý bạn là như thế này ?:

<T,S>(T t, S s)->...

Lambda này thuộc loại nào? Bạn không thể diễn đạt điều đó bằng Java và do đó không thể soạn biểu thức này trong một ứng dụng hàm và các biểu thức phải có thể ghép được.

Để hoạt động cần thiết này, bạn sẽ cần hỗ trợ cho các loại Rank2 trong Java.

Các phương thức được phép chung chung nhưng do đó bạn không thể sử dụng chúng làm biểu thức. Tuy nhiên, chúng có thể được giảm thành biểu thức lambda bằng cách chuyên biệt hóa tất cả các loại chung chung cần thiết trước khi bạn có thể chuyển chúng:ClassName::<TypeName>methodName


1
"Of what type is this lambda? You couldn't express that in Java..."Kiểu sẽ được suy ra bằng cách sử dụng ngữ cảnh, giống như với bất kỳ lambda nào khác. Loại lambda không được thể hiện rõ ràng trong chính lambda.
Kröw
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.