2015-02-13 28 views
18

tôi đang tìm kiếm thông qua mã nguồn Java cho giao diện Map và chạy vào đoạn này ít mã:Java biểu thức lambda, đúc, và comparators

/** 
    * Returns a comparator that compares {@link Map.Entry} in natural order on value. 
    * 
    * <p>The returned comparator is serializable and throws {@link 
    * NullPointerException} when comparing an entry with null values. 
    * 
    * @param <K> the type of the map keys 
    * @param <V> the {@link Comparable} type of the map values 
    * @return a comparator that compares {@link Map.Entry} in natural order on value. 
    * @see Comparable 
    * @since 1.8 
    */ 
    public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() { 
     return (Comparator<Map.Entry<K, V>> & Serializable) 
      (c1, c2) -> c1.getValue().compareTo(c2.getValue()); 
    } 

Từ khai báo phương thức tôi nhận được rằng đây là một phương pháp chung trả về một Trình so sánh của một kiểu được suy ra từ các mục nhập bản đồ được truyền vào nó hoặc được cung cấp một cách rõ ràng trong phương thức.

Điều thực sự khiến tôi tắt là giá trị trả lại. Có vẻ như biểu thức lambda

(c1, c2) -> c1.getValue().compareTo(c2.getValue()); 

được truyền rõ ràng tới Comparator<Map.Entry<K, V>>. Thê nay đung không?

Tôi cũng nhận thấy rằng dàn diễn viên rõ ràng bao gồm & Serializable. Tôi chưa từng thấy một giao diện kết hợp với một lớp trong một dàn diễn viên trước đây, nhưng có vẻ như các giá trị sau đây trong trình biên dịch:

((SubClass & AnInterface) anObject).interfaceMethod();

Mặc dù đây không làm việc:

public class Foo { 
    public static void main(String[] args) { 
     Object o = new Foo() { 
      public void bar() { 
       System.out.println("nope"); 
      } 
     }; 
     ((Foo & Bar) o).bar(); 
    } 
} 

interface Bar { 
    public void bar(); 
} 

Vì vậy, hai câu hỏi:

  1. Làm cách nào để thêm giao diện vào dàn diễn viên phải hoạt động? Điều này chỉ thực thi kiểu trả về của một phương thức của giao diện?

  2. Bạn có thể truyền một biểu thức Lambda lên Comparator không? Những gì khác họ có thể được đúc như? Hay là một biểu thức lambda về cơ bản chỉ là một Comparator? Ai đó có thể làm rõ tất cả điều này?

+2

Một lambda có thể được truyền tới bất kỳ giao diện nào bằng một phương thức trừu tượng duy nhất có chữ ký khớp với phần thân của lambda. –

+0

Aargh, 'Seralizable'! – fge

+2

http://www.java2s.com/Tutorials/Java/Java_Lambda/0080__Java_Intersection_Type.htm –

Trả lời

13

Làm cách nào để thêm giao diện vào dàn diễn viên phải hoạt động?

Điều này có cú pháp của một diễn viên, tuy nhiên nó thực sự xác định loại lambda bạn đang tạo thông qua giao diện kiểu. I E. bạn không tạo ra một thể hiện của một đối tượng mà sau đó được đúc sang một kiểu khác.

Điều này có thực thi kiểu trả về của phương thức giao diện không?

Điều này thực sự xác định loại lambda sẽ được tạo khi chạy. Có một số LambdaMetaFactory lấy loại này khi chạy và tạo mã phụ nếu loại này bao gồm Serializable.

Bạn có thể truyền một biểu thức Lambda vào một Trình so sánh không?

Bạn chỉ có thể truyền tham chiếu đến loại đối tượng đã có. Trong trường hợp này, bạn xác định lambda được tạo phải là Comparator. Bạn có thể sử dụng bất kỳ loại nào có chính xác một phương thức trừu tượng.

Hoặc là biểu thức lambda về cơ bản chỉ là một Trình so sánh?

Cùng một mã lambda có thể được sử dụng (sao chép + dán) trong các ngữ cảnh khác nhau và các giao diện khác nhau mà không thay đổi. Nó không phải là một Comparator như bạn sẽ thấy trong nhiều ví dụ khác trong JDK.

Tôi thấy thú vị là phương pháp count trên Stream.

+2

@sotirios cảm ơn bạn đã sửa chữa. –

+1

Chỉnh sửa thuật ngữ nhẹ: thay vì _defining_ loại lambda, chúng tôi gọi đây là _providing loại mục tiêu_ cho lambda. –

3

Theo Java Language Specification, các nhà điều hành dàn diễn viên (bất kể là trong ngoặc đơn) có thể là một ReferenceType Tiếp theo một hoặc nhiều từ AdditionalBound, ví dụ, một hoặc nhiều loại giao diện. Hơn nữa, spec cũng nói rằng Đó là một lỗi biên dịch thời gian nếu loại thời gian biên dịch của toán hạng có thể không bao giờ được đúc thành loại được chỉ định bởi toán tử cast theo quy tắc chuyển đổi đúc.

Trong trường hợp của bạn Foo không thực hiện Bar, nhưng thực tế điều này có thể không được rõ ràng tại thời gian biên dịch để bạn có được một ClassCastException vì mặc dù các phương pháp quy định tại các lớp nặc danh có chữ ký giống như một quy định tại Bar, đối tượng không thực hiện rõ ràng Bar. Hơn nữa, phương pháp quy định tại các lớp học vô danh được ẩn, trừ khi gọi trong báo cáo kết quả tương tự như khi chúng được định nghĩa, ví dụ,

new MyClass() { 
    void doSomethingAwesome() { /* ... */ } 
}.doSomethingAwesome(); 

công trình, nhưng điều này không:

MyClass awesome = new MyClass() { 
    void doSomethingAwesome() { /* ... */ } 
}; 
// undefined method, does not compile! 
awesome.doSomethingAwesome(); 
+0

Nó ném 'ClassCastException' nhưng nó biên dịch trong Java 8. Hãy nhớ rằng bạn có thể có một' lớp FooBar mở rộng Foo thực hiện Bar'. – Radiodef

+0

Điểm tốt. Sau đó, không có bất ngờ ở đây. –

13

Mặc dù Peter đã đưa ra một câu trả lời tuyệt vời, hãy để tôi thêm nhiều hơn nữa để rõ ràng hơn.

Một lambda có được loại chính xác chỉ khi khởi tạo. Điều này được dựa trên loại mục tiêu. Ví dụ:

Comparator<Integer> comp = (Integer c1, Integer c2) -> c1.compareTo(c2); 
BiFunction<Integer, Integer, Integer> c = (Integer c1, Integer c2) -> c1.compareTo(c2); 
Comparator<Integer> compS = (Comparator<Integer> & Serializable) (Integer c1, Integer c2) -> c1.compareTo(c2); 

Phía trên cùng một lambda trong cả 3 trường hợp, nhưng nó dựa trên loại tham chiếu bạn đã cung cấp. Do đó bạn có thể thiết lập cùng một lambda đến 3 loại khác nhau trên mỗi trường hợp.

Nhưng hãy nhớ bạn, một khi loại được đặt (trong khi khởi tạo) thì nó không thể thay đổi được nữa. Nó được hấp thụ ở mức bytecode. Vì vậy, rõ ràng là bạn không thể vượt qua c đến một phương thức dự kiến ​​Comparator vì một khi được khởi tạo thì chúng giống như các đối tượng java bình thường. (Bạn có thể nhìn vào class này để chơi xung quanh và tạo ra lambdas trên đường đi)


Vì vậy, trong trường hợp:

(Comparator<Map.Entry<K, V>> & Serializable) 
      (c1, c2) -> c1.getValue().compareTo(c2.getValue()); 

Lambda được khởi tạo với loại mục tiêu của nó như sánh và Serializable. Lưu ý kiểu trả về của phương thức chỉ là Comparator, nhưng vì Serializable cũng được ghi vào nó trong khi khởi tạo, nó luôn có thể được tuần tự hóa mặc dù thông báo này bị mất trong chữ ký phương thức.

Lưu ý rằng, truyền tới lambda khác với ((Foo & Bar) o).bar();. Trong trường hợp lambda, bạn đang khởi tạo lambda với kiểu của nó như là kiểu mục tiêu đã khai báo. Nhưng với ((Foo & Bar) o).bar();, bạn nhập kiểu biến o thành Foo và Bar. Trong trường hợp trước đây, bạn đang thiết lập loại. Trong trường hợp thứ hai, nó đã có một loại và bạn đang cố gắng vận may của bạn để đúc nó cho cái gì khác.Do đó trong cựu, nó ném ClassCastException bởi vì nó không thể chuyển đổi o để Bar

Làm thế nào để thêm một giao diện để một dàn diễn viên phải làm việc?

Đối tượng, giống như bình thường. Đối với lambda, giải thích ở trên.

Điều này có thực thi kiểu trả về của phương thức giao diện không?

No. Java không có Structural Types. Vì vậy, không có loại đặc biệt dựa trên phương pháp. Nó chỉ đơn giản cố gắng đúc o cho cả SO1Bar và nó không thành công vì sau

Bạn có thể đúc một biểu thức Lambda đến một Comparator? Những gì khác họ có thể được đúc như? Hay là một biểu thức lambda về cơ bản chỉ là một Comparator? Ai đó có thể làm rõ tất cả điều này?

Như đã giải thích ở trên. Một lambda có thể được khởi tạo cho bất kỳ FunctionalInterface nào dựa trên tất cả các giao diện đủ điều kiện cho lambda đó. Trong ví dụ trên, bạn rõ ràng không thể khởi tạo (c1, c2) -> c1.compareTo(c2) thành một số Predicate