Lambda i klasy anonimowe

Klasy anonimowe

Jeżeli przyjrzymy się metodzie forEach z klasy ForEachElem zobaczymy, że w specyfikacji metody jest:

void forEach(IntConsumer action)

a w przykładzie:

IntStream.of(tabl).forEach(val -> System.out.print(val + " "));

IntConsumer jest interfejsem funkcyjnym z jedną metodą funkcyjną:

void accept (int value)

Obiekt klasy implementującej interfejs IntConsumer możemy zastąpić klasą anonimową implementującą ten interfejs.

W listingu klasy Lambda1 zobaczymy

//nowa metoda 2
IntStream.of(tabl).forEach(new IntConsumer(){
	@Override
	public void accept(int value) {
		System.out.print(value + " ");
	}
});

To czy w wywołaniu metody użyjemy słowa ‘v’. ‘val’, ‘value’, czy jakiegokolwiek innego nie ma oczywiście żadnego znaczenia.

Jak widzimy:

IntStream.of(tabl).forEach(val -> System.out.print(val + " "));

‘val’ jest parametrem metody accept, a ciało wyrażenia lambda jest ciałem metody accept.

Typ wynikowy zwracany przez wyrażenie lambda jest określany automatycznie na podstawie kontekstu, w jakim to wyrażenie zostało użyte. Skoro użyta jest metoda forEach(), a w niej oczekiwany jest IntConsumer, mający metodę funkcyjną z argumentem int, oczekiwanym parametrem jest liczba typu int, a zwracanym typem oczywiście void, ponieważ metoda accept nie zwraca wyniku.

Moglibyśmy oczywiście zapisać tak:

IntStream.of(tabl).forEach((int value) -> System.out.print(value + " "));

ale jest to ‘mnożenie bytów ponad potrzeby’. Zwróć uwagę, że w przypadku wyraźnego podania typu musimy użyć nawiasów.

W listingu klasy ButtonPanel konstrukcję:

button.addActionListener(new ActionListener(){
	@Override
	public void actionPerformed(ActionEvent e) {
		button.setText("Kliknij jeszcze raz!");
	}
});

możemy zastąpić konstrukcją z wyrażeniem lamdba:

button.addActionListener(event -> button.setText("Kliknij jeszcze raz"));

Finalne zmienne lokalne

W lokalnych klasach anonimowych można używać zmiennych lokalnych w zakresie otaczającego kodu pod warunkiem, że były one oznakowane słowem kluczowym final. Wyrażenia lambda również mogą używać zmiennych finalnych.

Efektywnie finalne zmienne lokalne

Od wersji 8 wprowadzono pojęcia efektywnie finalnych zmiennych lokalnych, które mogą być używane przez wyrażenia lambda. Te zmienne niekoniecznie muszą być oznaczane słowem kluczowym final. Zmienna lokalna oznaczona słowem kluczowym final czy nie oznaczona, jest taką zmienną, która gdy jest zadeklarowana i zainicjalizowana, z pewnością nie ulegnie, gdy nie może ulec zmianie, ze względu na strukturę kodu i sposób użycia tej zmiennej w kodzie. Wyrażenie lambda używające takiej zmiennej nazywane jest przechwytującym wyrażeniem lamdba (capturing lambda). Nazwa bierze się stąd, że kompilator wychwytuje wartości zmiennych lokalnych np. w metodzie i upewnia się, że te zmienne będą mogły być użyte, gdyby ewentualnie było wykonywane wyrażenie lambda, co często zachodzi, gdy zasięg zmiennej już nie istnieje bo metoda zakończyła już działanie, a wartość zmiennej wciąż jest potrzebna dla wyrażenia lambda.

W listingu klasy Sheep zmienna podawana do metody setPCukru jest finalna.

void setPCukru(final int p) {. . .}

Obecnie nie musi (choć może) być oznaczona jako final gdyż jest zmienną efektywnie finalną. W tym kodzie wartość p jest sprawdzana i użyta, ale nigdzie nie ma możliwości jej zmiany. Gdybyśmy spróbowali tak zmienić kod, aby umożliwić – choćby potencjalnie –
zmianę tej wartości – kompilator wyrzuciłby błąd.

this w wyrażeniach lambda

Jak wspomnieliśmy w przypomnieniu wiadomości, gdy z klasy wewnętrznej chcieliśmy odnieść się do klasy otaczającej musieliśmy u klasie wewnętrznej użyć konstrukcji:

Sheep s = Sheep.this;

czyli słowa this poprzedzonego nazwą klasy zewnętrznej. Słowo this użyte bez nazwy klasy zewnętrznej oznaczałoby obiekt typu klasy anonimowej wewnętrznej czyli typu Brain (zajrzyj do klasy Sheep1. W wyrażeniach lambda, gdy użyjesz słowa kluczowego this bez poprzedzającej nazwy klasy to słowo to odnosi się zawsze do obiektu klasy zewnętrznej.

Nazwy parametrów i zmiennych w wyrażeniach lambda

Nazwy parametrów i zmiennych używanych w wyrażeniu lambda nie mogą się pokrywać z nazwami zmiennych lokalnych istniejących w zasięgu tego wyrażenia lambda.

Pliki do ściągnięcia

lambdas1.zip (pakiet)

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *