2017-01-17 7 views
5

Używam lamdbas, dzięki czemu mogę konsekwentnie ustawiać właściwości obiektu ModelObject według wartości, które mogę pobrać z trzech różnych obiektów. Kod działa tak:Przydział dwukropka w metodzie z dwoma parametrami

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); 
} 

Czytałem here że możliwe jest przepisanie bar(value -> model.setB(value), bObject); do bar(model::setB, bObject). Myślę, że to wygląda lepiej i bardziej zwięźle, ale nie znalazłem sposobu na przepisanie metody setA do podwójnego notacji. Czy ktoś może mi powiedzieć, czy jest to możliwe, a jeśli tak, to w jaki sposób?

+2

Proszę odnieść się do tego [Link do odnośników metod] (https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html). Pokazuje wszystkie rodzaje odniesień do metod. Mam nadzieję, że to pomaga –

+0

Nie sądzę, że jest to możliwe. – toongeorges

Odpowiedz

5

z https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html i https://www.codementor.io/eh3rrera/tutorials/using-java-8-method-reference-du10866vx

Byłoby 4 różne rodzaje odniesienia metod. Odpowiednią N i sposób odniesienia:

  • (arg) -> Class.staticMethod (arg), klasa :: staticmethod
  • (obj, args) -> obj.instanceMethod (arg) ObjectType :: instanceMethod
  • (args) -> obj.instanceMethod (args), OBJ :: instanceMethod
  • (args) -> nowe className (args), className :: nowe

wartość lambda -> Model .setA (CONSTANT, value) czy n Odpowiada to dowolnemu z powyższych lambd, więc nie można go przepisać jako odwołania do metody.

+0

Powodem, dla którego przyjęłam tę odpowiedź jest: podczas gdy inne odpowiedzi są sprytne i użyteczne, nie powodują, że mój kod jest bardziej zrozumiały lub czystszy. Więc pozostanę przy użyciu notacji '->". –

2

Nie w taki sposób, jak jest zapisany interfejs funkcjonalny Setter. W przeciwieństwie do setB i setC, metoda setA oczekuje dwóch argumentów, podczas gdy interfejs Setter ma metodę oczekującą tylko jednego argumentu. Możesz dodać inny interfejs, który przyjmuje dwa argumenty:

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

Następnie można to nazwać metodą bar:

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

Następnie można wywołać bar następująco:

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

Uwaga: Możesz zachować inną metodę bar. Nowa może być przeciążeniem.

+0

Co jednak należy zrobić z innymi wywołaniami 'bar', że OP został wymieniony bez parametru' CONSTANT'? – Sweeper

+1

@Sweeper Może zachować inną metodę "słupkową". To po prostu przeciążona metoda. – manouti

4

Aby użyć podwójnej notacji dwukropka, metoda, do której się odwołujesz, musi mieć ten sam podpis, co wymagana metoda. Nie można więc używać :: chyba że zmienisz IModel:

Możesz dodać przeciążenie setA w IModel:

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

Następnie można odwoływać się, że przeciążenie:

bar(model::setA, aObject); 

.

+0

Aby zmienić 'IModel' byłoby zbyt dużo pracy, aby użyć czystszego zapisu w tym przypadku. Nie widziałem wcześniej słowa kluczowego "default", więc przyjrzę się temu w przyszłości! –

2

Nie potrzebujesz specjalnego interfejsu: setB to Consumer<String> i setA to BiConsumer<String, String>. Następnie można dostosować BiConsumer Do Consumer:

albo z metody domyślnej w interfejsie (jeśli setA jest zawsze wywołana stałą, dlaczego nie?):

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); 
} 

albo przy użyciu adaptera z BiConsumer do 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); 
} 

i używając go tak:

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

Uwaga: użyłem adapt jako nazwa przykład, ale to złe imię, kiedy go mieszać z innymi adapt metody przeciążeniem (bo rodzajowy i typu skasowaniem). Osobiście nazywam to jak fixLeftValue.

Pamiętaj, że adapt będzie generowany przy każdym wywołaniu foo.

Powiązane problemy