2017-05-29 23 views
5

Tôi đã có một câu hỏi về việc tái sử dụng các biểu thức lambda mà không trùng lặp mã. Ví dụ, nếu tôi có một phương pháp trợ giúp tôi có thể dễ dàng mã nó như là một phương pháp tĩnh và có thể tham khảo nó từ các lớp khác mà không cần sao chép mã. Làm thế nào điều này sẽ làm việc trong biểu thức lambda? Ví dụ: Tôi có phương pháp tĩnh sau bằng văn bảnbiểu thức lambda vs phương pháp tĩnh

public class MyUtil { 

    public static int doubleMe(int x) { 
     return x * 2; 
    } 
} 

tôi có thể tái sử dụng cùng một phương pháp không có mã trùng lặp ở nhiều nơi trên khắp các dự án

public class A { 

    public void someOtherCalculation() { 
     MyUtil.doubleMe(5); 
    } 
} 

public class B { 

    public void myCalculation() { 
     MyUtil.doubleMe(3); 
    } 
} 

Làm thế nào nó sẽ làm việc khi nói đến một hàm lambda , viết hàm một lần và sử dụng cùng một lúc ở nhiều lớp.

Function<Integer, Integer> doubleFunction = x -> x * 2; 

Trong ví dụ của tôi, tôi sẽ viết hàm lambda ở trên và cách tôi sử dụng lại cùng lớp A và B?

+3

"Sử dụng lại mã" là mục tiêu sai ở đây. Mã _expressiveness_ là những gì cần thiết. Những gì có thể gây hại là có từ việc sử dụng một lambda mà không "tái sử dụng" mã, và những bằng chứng nào bạn có mà nó không? Lambdas là khi nhiều functors khác nhau có thể thay thế trong một thuật toán. Bạn sẽ viết một phương pháp tĩnh rõ ràng cho tất cả chúng, và tạo ra một tham chiếu phương pháp cho mỗi chỉ để bạn có thể nói, "Hãy nhìn xem, mẹ, tôi đang sử dụng lambdas!"? Nó không có ý nghĩa hơn chỉ để sử dụng lambdas thông thường trong trường hợp như vậy? Lợi ích kỹ thuật _real_ bạn tìm kiếm là gì? –

+0

Nếu bạn muốn sử dụng lại một hàm, sau đó xác định một lớp thực hiện giao diện Function. Một phương thức tĩnh không phải là một hàm. – xgbuils

Trả lời

6

tôi sẽ viết hàm lambda trên đâu

Kể từ khi chức năng của bạn không tham khảo bất kỳ lĩnh vực, nó là thích hợp để đặt nó trong một lĩnh vực thức tĩnh:

class Utility { 
    public static final Function<Integer,Integer> doubleFunction = x -> x * 2; 
} 

Tôi sẽ sử dụng lại như thế nào trong lớp A và B?

Bạn sẽ coi nó như Utility.doubleFunction, và vượt qua nó trong bối cảnh nơi nó được yêu cầu:

callMethodWithLambda(Utility.doubleFunction); 

Lưu ý rằng phương pháp tài liệu tham khảo cho phép bạn định nghĩa một hàm, và sử dụng nó như thể nó là lambda :

class Utility { 
    public static Integer doubleFunction(Integer x) { 
     return x*2; 
    } 
} 
... 
callMethodWithLambda(Utility::doubleFunction); 

Phương pháp này rất linh hoạt, vì nó cho phép bạn sử dụng lại cùng một mã trong nhiều ngữ cảnh khi bạn thấy phù hợp.

3

Thực sự, các chức năng ẩn danh là dành cho các trường hợp sử dụng lại mã không phải là cần thiết.

Ví dụ câm, nhưng giả sử bạn đang sử dụng map để thêm hai vào mỗi số trong danh sách. Nếu đây là một hành động phổ biến mà bạn có thể cần trên tất cả các nơi, một chức năng tĩnh mà thêm hai đến một số có ý nghĩa hơn so với việc viết cùng một lambda ở khắp mọi nơi.

Nếu, tuy nhiên bạn có một hàm duy nhất thêm hai vào danh sách, nên xác định hàm "thêm hai" cục bộ dưới dạng lambda để bạn không cắm lớp của mình với mã không cần ở bất kỳ đâu khác.

Khi viết Clojure, sử dụng rộng rãi các chức năng bậc cao, việc tạo các hàm ẩn danh cục bộ để làm gọn mã trong hàm "đầy đủ" mà tôi đang viết là khá phổ biến. Phần lớn các chức năng ẩn danh này sẽ không mang tính nhạy cảm trong phạm vi "toàn cầu" (hoặc phạm vi lớp); đặc biệt là vì chúng thường có các bao đóng trên các biến cục bộ, nên chúng không thể là anyways toàn cục.

+1

Tôi đã luôn luôn giả định điều này, nhưng không bao giờ đọc nó ở bất cứ đâu. Rất hữu ích. – ftor

2

Với biểu thức lambda, bạn không cần phải lo lắng về khả năng sử dụng lại (trên thực tế, hầu hết các lambda không được sử dụng lại).Nếu bạn muốn có một con trỏ Function để trỏ đến phương pháp này, bạn có thể tuyên bố một mặt hàng như dưới đây:

Function<Integer, Integer> doubleFunction = MyUtil::doubleMe; 

Và vượt qua nó để phương pháp nào hoặc dòng để áp dụng/bản đồ, ví dụ:

public static void consume(Function<Integer, Integer> consumer, int value){ 
    System.out.println(consumer.apply(value)); 
} 

public static void main(String[] args) throws Exception{ 
    Function<Integer, Integer> doubleFunction = MyUtil::doubleMe; 
    consume(doubleFunction, 5); 
} 
2

Khác các câu trả lời khác. Tôi muốn trả lời câu hỏi của bạn theo cách TDD.

NẾUdoubleMe đơn giản như bạn đã viết, bạn nên ngừng sử dụng tham chiếu biểu thức phương thức và chỉ gọi trực tiếp là lời gọi phương thức chung.

NẾUdoubleMe của bạn rất phức tạp mà bạn muốn kiểm tra doubleMe độc lập, bạn cần phải thực hiện phụ thuộc ngầm explicity bởi dependency injection để kiểm tra liệu họ có thể làm việc với nhau bằng giao thức cummunication của họ. Nhưng java không thể tham chiếu một phương thức ngoại lệ, ngoại trừ bạn sử dụng phản chiếu api Method/sử dụng lớp ẩn danh thực hiện giao diện SAM ủy quyền yêu cầu phương thức trước trong jdk-8. Điều hạnh phúc là bạn có thể tham chiếu một biểu thức phương thức tham chiếu đến một giao diện chức năng trong jdk-8. vì vậy bạn có thể làm cho phụ thuộc ngầm rõ ràng bằng cách sử dụng giao diện chức năng, sau đó tôi muốn viết một số thử nghiệm giao thức truyền thông như sau:

@Test 
void applyingMultiplicationWhenCalculating???(){ 
    IntUnaryOperator multiplication = mock(IntUnaryOperator.class); 
    B it = new B(multiplication); 

    it.myCalculation(); 

    verify(multiplication).applyAsInt(3); 
} 

sau đó lớp học của bạn như là B áp dụng dependency injection là giống như như sau:

public class B { 
    IntUnaryOperator multiplication; 

    public B(IntUnaryOperator multiplication){ 
      this.multiplication = multiplication; 
    } 

    public void myCalculation() { 
      multiplication.applyAsInt(3); 
    } 
} 

THEN bạn có thể tái sử dụng một phương pháp mà theo giới thiệu một tài liệu tham khảo biểu hiện phương pháp để một giao diện chức năng như sau:

A a = new A(MyUtil::doubleMe); 
B b = new B(MyUtil::doubleMe); 
Các vấn đề liên quan