2017-01-17 19 views
5

Tôi đang sử dụng lamdbas để tôi có thể thiết lập một cách nhất quán các thuộc tính của một ModelObject theo các giá trị tôi có thể lấy từ ba đối tượng khác nhau. Mã này hoạt động như thế này:phép gán dấu hai chấm trên phương thức có hai tham số

public class Processor { 

    private void bar(Setter setter, MyClass myObject) { 
     String variable = myObject.getStringByABunchOfMethods(); 
     setter.setVariable(variable); 
    } 

    protected void foo(...) { 
     ... 
     bar(value -> model.setA(CONSTANT, value), aObject); 
     bar(value -> model.setB(value), bObject); 
     bar(value -> model.setC(value), cObject); 
     ... 
    } 

    private interface Setter { 
     public void setVariable(String string); 
    } 

} 

public interface IModel { 
    public void setA(String arg0, String arg1); 
    public void setB(String arg0); 
    public void setC(String arg0); 
} 

Tôi đã đọc here rằng người ta có thể viết lại bar(value -> model.setB(value), bObject); để bar(model::setB, bObject). Tôi nghĩ rằng điều này có vẻ tốt hơn và ngắn gọn hơn, nhưng tôi đã không tìm thấy một cách để viết lại phương thức setA thành ký hiệu kép ::. Bất cứ ai có thể cho tôi biết nếu điều này là có thể, và nếu như vậy: làm thế nào là điều này có thể?

+2

Vui lòng tham khảo [Liên kết tham chiếu phương pháp] (https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html). Hiển thị tất cả các loại tham chiếu phương thức. Hy vọng điều này sẽ giúp –

+0

Tôi không nghĩ rằng điều đó là có thể. – toongeorges

Trả lời

5

từ https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.htmlhttps://www.codementor.io/eh3rrera/tutorials/using-java-8-method-reference-du10866vx

Sẽ có 4 loại khác nhau của tài liệu tham khảo phương pháp. Các lambda và phương pháp tương ứng tham khảo:

  • (args) -> Class.staticMethod (args), Class :: staticMethod
  • (obj, args) -> obj.instanceMethod (args), ObjectType :: instanceMethod
  • (args) -> obj.instanceMethod (args), obj :: instanceMethod
  • (args) -> mới ClassName (args), ClassName :: mới

giá trị lambda -> mô hình .setA (CONSTANT, giá trị) có n ot tương ứng với bất kỳ lambdas nào ở trên, vì vậy không thể viết lại nó như là một tham chiếu phương thức.

+0

Lý do tôi chấp nhận câu trả lời này là: trong khi các câu trả lời khác thông minh và hữu ích, chúng không làm cho mã của tôi dễ hiểu hơn hoặc rõ ràng hơn. Vì vậy, tôi sẽ sử dụng ký hiệu '->'. –

2

Không theo cách giao diện chức năng Setter được viết. Không giống như setBsetC, phương thức setA mong đợi hai đối số trong khi giao diện Setter có phương thức chỉ mong đợi một đối số. Bạn có thể thêm một giao diện mà chấp nhận hai đối số:

private interface SetterWithDefault { 
    public void setVariable(String defaultString, String string); 
} 

Sau đó, bạn có thể gọi nó là thông qua phương thức bar:

private void bar(SetterWithDefault setter, String defaultString, MyClass myObject) { 
    String variable = myObject.getStringByABunchOfMethods(); 
    setter.setVariable(defaultString, variable); 
} 

Sau đó, bạn có thể gọi bar như sau:

bar(model::setA, CONSTANT, aObject); 

Lưu ý: Bạn có thể giữ phương thức bar khác. Cái mới có thể là một tình trạng quá tải.

+0

Nhưng những gì nên được thực hiện với các lệnh 'bar' khác mà OP được liệt kê không có tham số' CONSTANT'? – Sweeper

+1

@Sweeper Anh ta có thể giữ phương thức 'bar' khác. Nó chỉ đơn giản là một phương pháp quá tải. – manouti

4

Để sử dụng ký hiệu dấu hai chấm, phương pháp mà bạn đang tham chiếu phải có cùng chữ ký với phương thức được yêu cầu. Vì vậy, bạn không thể sử dụng :: trừ khi bạn thay đổi IModel của bạn:

Bạn có thể thêm một tình trạng quá tải của setA trong IModel:

default void setA(String arg0) { 
    setA(CONSTANT, arg0); 
} 

Sau đó, bạn có thể tham khảo rằng tình trạng quá tải:

bar(model::setA, aObject); 

.

+0

Để thay đổi 'IModel' sẽ là quá nhiều công việc để sử dụng ký hiệu sạch hơn trong trường hợp này. Tôi đã không nhìn thấy từ khóa 'mặc định' trước đây, vì vậy tôi sẽ xem xét điều đó cho tương lai! –

2

Bạn không cần giao diện đặc biệt: setBConsumer<String>setABiConsumer<String, String>. Sau đó bạn có thể thích ứng một BiConsumer đến một Consumer:

Hoặc với một phương pháp mặc định trong giao diện của bạn (nếu setA luôn được gọi với một hằng số, tại sao không?):

interface Model { 
    public void setA(String arg0, String arg1); 
    default void setA(String arg1) {setA(CONSTANT, arg1);} 
    public void setB(String arg0); 
    public void setC(String arg0); 
} 

Hoặc sử dụng một bộ chuyển đổi của BiConsumer để Consumer:

static <T, U> Consumer<V> adapt(T t, BiConsumer<T, U> biConsumer) { 
    Objects.requireNonNull(biConsumer, "biConsumer"); 
    Objects.requireNonNull(t, "t"); 
    return t -> biConsummer.accept(t, u); 
} 

Và sử dụng nó như thế này:

protected void foo(...) { 
    ... 
    bar(adapt(CONSTANT, model::setA), aObject); 
    bar(model::setB, bObject); 
    bar(model::setC, cObject); 
    ... 
} 

Lưu ý: Tôi đã sử dụng adapt làm tên ví dụ, nhưng đó là tên xấu khi bạn trộn với phương thức quá tải khác adapt (vì loại bỏ chung và loại). Cá nhân tôi tên nó giống như fixLeftValue.

Hãy coi chừng rằng adapt sẽ được tạo mỗi khi bạn gọi foo.

Các vấn đề liên quan