Lamda i referencje do metod statycznych

0

Witajcie!

Uczę się ostatnio lambdy, jestem na etapie wykorzystywania z referencjami.
Oto przykład z kompendium Herberta Schildta z użyciem lambdy z referencją do metody statycznej (to pierwszy przykład w kompendium, dalej są metody instancyjne, kontruktory...).
O ile wykorzystanie metody instancyjnej w lambdzie mnie dziwi (w końcu i tak tworzymy obiekt, to dlaczego z niego nie skorzystać?) to metody statyczne spowodowały u mnie mały zawrót głowy.
Po co tworzyć tak pogmatwany scenariusz skoro można się odwołać do metody bezpośrednio?

[Od razu dodatkowe pytanie - jeśli znacie jakieś strony, które tłumaczą po co z tego korzystać?/jak umiejętnie z tego korzystać?/kiedy z tego korzystać? a nie jak to się robi? względem javy będę bardzo wdzięczny]
Jeżeli pytanie jest głupie to z góry przepraszam.

2

Czasami takie lambdy/metody są używane przez wiele klas w kilku miejscach - taka kolekcja utilsów. Wtedy można je wepchać do staticów (cokolwiek by nie mówić o utilsach złego -> po prostu się zdażają). Comparatory, helpery do kolekcji, konwertery itp.

W przykładzie widać taką powiedzmy "biblioteczną" funkcję do Stringów.

0

Mógłbyś tylko jeszcze powiedzieć co rozumiesz przez kolekcję utilsów?

1

Jakie jest pytanie? Czy pytasz "po co tworzyć metody statyczne?", czy też "po co odwoływać się do metod statycznych przez lambdy/method handlery?"

Ps. mylisz wyrażenie lambda z method handlerem:

Function<String, String> lambdaFunc = s -> this.doSmthg(s); // lambda
Function<String, String> mhFunc = this::doSmthg; // method handler
1

Używamy method reference ponieważ typ oczekiwany dla stringOp to StringFunc, a nie String czy MyStringOps. Zatem w tym przypadku metoda statyczna zostanie opakowana w odpowiedni typ (efektywnie zrobione zostanie invokedynamic, które bada tylko zgodność na poziomie sygnatur w metodzie, a nie typu obiektu z którego metoda pochodzi).

Użycie metRef jest tu po prostu jedyną dozwoloną opcją. Ponad to zauważ, że możemy w ten sposób w ogóle nie wywoływać tej metody jeżeli dodamy jakiś if w stringOp.

Dlaczego static? Ano czasami mamy np. Commons Collections z ichnimy CollectionUtils, gdzie same statyki, a ty chcesz jakiś zaaplikować.

2

@Koziołek: Nie rozumiem co masz na myśli - "Użycie metRef jest tu po prostu jedyną dozwoloną opcją."
przecież w powyższym przykładzie można napisać

outStr = stringOp(a -> a.substring(4), inStr);
1

@jarekr000000: masz rację. Można jeszcze użyć funkcji anonimowej, ale to będzie tylko sztuka dla sztuki:

outStr = stringOp(s-> MyStringOps.strReverse(s), inStr);

Jedynym plusem, choć nie koniecznie, jest ujawnienie, że strReverse jest statyczna. No chyba, że ktoś nie trzyma się konwencji nazewniczej w javie i twardo pisze nazwy zmiennych z wielką literą na początku.

To co miałem na myśli, to użycie method ref jako jedyne rozwiązanie, które nie narusza istniejącego API np. nie zmienia statyka na niestatyka.

0

Ps. mylisz wyrażenie lambda z method handlerem - a to nie to samo (tylko różni się kwestią zapisu)? W końcu odwołujesz się do tej samej metody, przekazywany argument jest taki sam...
Resztę (chyba) zrozumiałem, dziękuję @jarekr000000 @Koziołek @wartek01. męczy mnie tylko jedno - po co odwoływać się do metod statycznych przez lambdy/method handlery? (tak jak poprosił o uszczególnienie @wartek01 ). W końcu wymaga to tworzenia interfejsu funkcyjnego (co często mi także wydaje się zbędne, choć nie zawsze), odpowiedniej klasy z wywołaniem funkcji, której argumentem będzie tenże interfejs...
Troszkę się to robi coraz bardziej pogmatwane :(

1

@Burdzi0: by nie duplikować kodu, bo sygnatura metody statycznej (typ zwracany) nie pasuje do typu oczekiwanego np. oczekujesz Function<Int, String>, a metoda zwraca Converter.asString(Int):String.

Co do zapisu, to method ref i funkcja anonimowa różnią się w bytecodzie, ale w tym przypadku to pomijalny szczegół.

0

Ostatnie pytanie, wynikające z mojej niewiedzy - co oznacza zapis Converter.asString(Int):String, a konkretnie do czego służy dwukropek (:String)? Pierwszy raz go na oczy widzę (pewnie wstyd jak nic)

EDIT
Okej, znalazłem (dla ciekawych tutaj).
Dziękuję serdecznie wszystkim, którzy się udzielili w temacie :)

1
Burdzi0 napisał(a):

po co odwoływać się do metod statycznych przez lambdy/method handlery? W końcu wymaga to tworzenia interfejsu funkcyjnego(...)

Nie musisz tworzyć interfejsu funkcyjnego bo Java już takowe posiada (spójrz: Function, Consumer, Supplier, Runnable, Callable itp. ) - możesz je po prostu zaimplementować. Prosta rzecz:

 public String replaceValues(String argument, int mapType){
	Function<String, String> mapper;
	if(mapType == 1){
		mapper = MyClass::firstMap;
	} else {
		mapper = MyClass::secondMap;
	}

	return mapper.apply(argument);
}

private static String firstMap(String s){
	return s.replaceAll("A", "B");
}

private static String secondMap(String s){
	return s.replaceAll("A", "C");
}

Oczywiście jest to przykład mocno uproszczony. Jednakże traktując metody - także statyczne - jako obiekty, które można sobie przekazywać itp. można wiele rzeczy fajnych zrobić.

Drugą opcją jest zamiana lambda statement na lambda expression, tj. kiedy metoda jest za długa żeby zapisać ją w jednej linijce to można ją uprościć na potrzeby czytelności kodu:

Np. takie coś:

    public Collection<String> process(Collection<String> strings){
        return strings.stream()
                .map(s -> {
                            StringBuffer padded = new StringBuffer(s);
                            while (padded.length() < 5) {
                                padded.append("0");
                            }

                            return padded.toString();
                        }
                ).collect(Collectors.toSet());
    }

wygląda gorzej niż:

    public Collection<String> process2(Collection<String> strings){
        return strings.stream()
                .map(Test::pad)
                .collect(Collectors.toSet());
    }

    public static String pad(String str){
        StringBuffer padded = new StringBuffer(str);
        while (padded.length() < 5){
            padded.append("0");
        }
        return padded.toString();
    }

Zwłaszcza, jeżeli takich mapowań w stream'ie byłoby więcej

1 użytkowników online, w tym zalogowanych: 0, gości: 1