Mam metodę, która ma około dziesięciu wierszy kodu. Chcę stworzyć więcej metod, które robią dokładnie to samo, z wyjątkiem niewielkich obliczeń, które zmienią jeden wiersz kodu. Jest to idealna aplikacja do przekazywania wskaźnika funkcji w celu zastąpienia tej jednej linii, ale Java nie ma wskaźników funkcji. Jaka jest moja najlepsza alternatywa?
java
closures
function-pointers
Bill jaszczurka
źródło
źródło
::
drugiej strony operator ...Odpowiedzi:
Anonimowa klasa wewnętrzna
Załóżmy, że chcesz przekazać funkcję z
String
parametrem, który zwraca anint
.Najpierw musisz zdefiniować interfejs z funkcją jako jego jedynym elementem, jeśli nie możesz ponownie użyć istniejącego.
Metoda, która pobiera wskaźnik, zaakceptowałaby
StringFunction
instancję w następujący sposób:I nazwano by tak:
EDYCJA: W Javie 8 można to nazwać wyrażeniem lambda:
źródło
Dla każdego „wskaźnika funkcji” stworzyłbym małą klasę funktorów, która implementuje twoje obliczenia. Zdefiniuj interfejs, który będą implementować wszystkie klasy, i przekaż instancje tych obiektów do swojej większej funkcji. Jest to kombinacja „ wzorca poleceń ” i „ wzorca strategii ”.
Przykład @ sblundy jest dobry.
źródło
Gdy istnieje wstępnie zdefiniowana liczba różnych obliczeń, które można wykonać w tej jednej linii, użycie wyliczenia jest szybkim, ale jasnym sposobem na wdrożenie wzorca strategii.
Oczywiście deklaracja metody strategii, a także dokładnie jedna instancja każdej implementacji są zdefiniowane w jednej klasie / pliku.
źródło
Musisz stworzyć interfejs zapewniający funkcje, które chcesz przekazać. na przykład:
Następnie, gdy musisz przekazać funkcję, możesz zaimplementować ten interfejs:
Wreszcie funkcja mapy korzysta z funkcji przekazanej w funkcji 1 w następujący sposób:
Często możesz użyć Runnable zamiast własnego interfejsu, jeśli nie musisz przekazywać parametrów, lub możesz użyć różnych innych technik, aby parametry były mniej „ustalone”, ale zwykle jest to kompromis z bezpieczeństwem typu. (Lub możesz przesłonić konstruktor, aby obiekt funkcji przekazywał parametry w ten sposób .. istnieje wiele podejść, a niektóre działają lepiej w pewnych okolicznościach.)
źródło
Odniesienia do metod za pomocą
::
operatoraW argumentach metody można używać odwołań do metod, w których metoda akceptuje interfejs funkcjonalny . Interfejs funkcjonalny to dowolny interfejs, który zawiera tylko jedną metodę abstrakcyjną. (Interfejs funkcjonalny może zawierać jedną lub więcej metod domyślnych lub metod statycznych.)
IntBinaryOperator
to funkcjonalny interfejs. Jego abstrakcyjna metodaapplyAsInt
przyjmuje dwaint
s jako parametry i zwraca anint
.Math.max
akceptuje również dwaint
si zwraca anint
. W tym przykładzieA.method(Math::max);
sprawia , żeparameter.applyAsInt
wysyła do niego dwie wartości wejścioweMath.max
i zwraca wynikMath.max
.Ogólnie możesz użyć:
zamiast:
co jest skrótem od:
Aby uzyskać więcej informacji, zobacz :: :: operator (dwukropek) w Javie 8 i specyfikacji języka Java §15.13 .
źródło
Możesz to również zrobić (co w niektórych rzadkich przypadkach ma sens). Problem (i jest to duży problem) polega na tym, że tracisz wszelkie bezpieczeństwo korzystania z klasy / interfejsu i musisz zająć się przypadkiem, w którym metoda nie istnieje.
Ma tę „zaletę”, że można zignorować ograniczenia dostępu i wywoływać metody prywatne (nie pokazano w tym przykładzie, ale można wywoływać metody, których kompilator normalnie nie pozwala na wywołanie).
Ponownie, jest to rzadki przypadek, który ma sens, ale przy takich okazjach jest to miłe narzędzie.
źródło
Jeśli masz tylko jedną linię, która jest inna, możesz dodać parametr, taki jak flaga i instrukcja if (flag), która wywołuje jedną linię lub drugą.
źródło
Być może zainteresuje Cię informacja o pracach nad Javą 7 obejmujących zamknięcia:
Jaki jest obecny stan zamknięć w Javie?
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures
źródło
Nowe interfejsy funkcjonalne Java 8 i odniesienia do metod za pomocą
::
operatora.Java 8 jest w stanie zachować odwołania do metod (MyClass :: new) za pomocą wskaźników „ @ Functional Interface ”. Nie ma potrzeby używania tej samej nazwy metody, wymagany jest tylko ten sam podpis metody.
Przykład:
Co tu mamy?
NALEŻY KORZYSTAĆ Z INTERFEJSÓW FUNKCJONALNYCH TYLKO DLA SŁUCHAWCÓW I TYLKO DO TYCH!
Ponieważ wszystkie inne takie wskaźniki funkcji są bardzo złe dla czytelności kodu i zdolności do zrozumienia. Jednak czasami przydatne są bezpośrednie odniesienia do metod, na przykład foreach.
Istnieje kilka predefiniowanych interfejsów funkcjonalnych:
We wcześniejszych wersjach Java powinieneś wypróbować biblioteki Guava, które mają podobną funkcjonalność i składnię, jak wspomniano powyżej Adrian Petrescu.
Aby uzyskać dodatkowe badania, zobacz Arkusz Java 8
i dzięki The Guy with The Hat za link do specyfikacji języka Java §15.13 .
źródło
Odpowiedź @ sblundy jest świetna, ale anonimowe klasy wewnętrzne mają dwie małe wady, z których pierwszą jest to, że nie nadają się do ponownego użycia, a wtórna to nieporęczna składnia.
Zaletą jest to, że jego wzór rozszerza się na pełne klasy bez żadnych zmian w klasie głównej (tej, która wykonuje obliczenia).
Kiedy tworzysz nową klasę, możesz przekazać parametry do tej klasy, która może działać jako stałe w twoim równaniu - więc jeśli jedna z twoich klas wewnętrznych wygląda tak:
ale czasami potrzebujesz takiego, który jest:
a może trzecia to:
zamiast tworzyć dwie anonimowe klasy wewnętrzne lub dodawać parametr „przejście”, możesz utworzyć jedną RZECZYWISTĄ klasę, którą tworzysz jako:
Po prostu zapisałby stałą w klasie i używałby jej w metodzie określonej w interfejsie.
W rzeczywistości, jeśli WIESZ, że twoja funkcja nie zostanie zapisana / ponownie użyta, możesz to zrobić:
Ale niezmienne klasy są bezpieczniejsze - nie mogę znaleźć uzasadnienia, aby uczynić taką klasę zmienną.
Naprawdę publikuję to tylko dlatego, że wzdrygam się za każdym razem, gdy słyszę anonimową klasę wewnętrzną - widziałem dużo zbędnego kodu, który był „wymagany”, ponieważ pierwszą rzeczą, którą zrobił programista, był anonimowy, gdy powinien był użyć prawdziwej klasy i nigdy przemyślał swoją decyzję.
źródło
Biblioteki Google Guava , które stają się bardzo popularne, mają ogólny obiekt Function i Predicate , który przepracowali w wielu częściach swojego API.
źródło
Brzmi dla mnie jak wzorzec strategii. Sprawdź wzorce Java fluffycat.com.
źródło
Jedną z rzeczy, których naprawdę brakuje mi podczas programowania w Javie, są wywołania zwrotne funkcji. Jedną z sytuacji, w której potrzeba ich ciągłego prezentowania się była w rekurencyjnym przetwarzaniu hierarchii, w której chcesz wykonać określoną akcję dla każdego elementu. Jak chodzenie po drzewie katalogów lub przetwarzanie struktury danych. Minimalista we mnie nie lubi definiować interfejsu, a następnie implementacji dla każdego konkretnego przypadku.
Pewnego dnia zastanawiałem się, dlaczego nie? Mamy wskaźniki metod - obiekt Method. Dzięki optymalizacji kompilatorów JIT wywoływanie odblaskowe nie powoduje już znacznej utraty wydajności. A oprócz, powiedzmy, kopiowania pliku z jednej lokalizacji do drugiej, koszt wywołanej metody odbicia blednie.
Gdy się nad tym zastanowiłem, zdałem sobie sprawę, że wywołanie zwrotne w paradygmacie OOP wymaga powiązania obiektu i metody razem - wprowadź obiekt wywołania zwrotnego.
Sprawdź moje rozwiązanie oparte na odbiciu dla wywołań zwrotnych w Javie . Bezpłatnie do dowolnego użytku.
źródło
OK, ten wątek jest już wystarczająco stary, więc bardzo prawdopodobne, że moja odpowiedź nie jest pomocna w przypadku pytania. Ale ponieważ ten wątek pomógł mi znaleźć moje rozwiązanie, i tak go tutaj opublikuję.
Musiałem użyć zmiennej metody statycznej ze znanym wejściem i znanym wyjściem (oba podwójne ). Zatem znając pakiet metod i nazwę, mógłbym pracować w następujący sposób:
dla funkcji, która przyjmuje jedno podwójne jako parametr.
Tak więc w mojej konkretnej sytuacji zainicjowałem go
i wywołał go później w bardziej złożonej sytuacji z
gdzie aktywność jest dowolną podwójną wartością. Zastanawiam się nad tym, by zrobić to nieco bardziej abstrakcyjnie i uogólnić, tak jak zrobiło to SoftwareMonkey, ale obecnie jestem wystarczająco zadowolony z tego, jaki jest. Trzy linie kodu, brak klas i interfejsów, to nie jest takie złe.
źródło
code
przeceny, byłem zbyt niecierpliwy i głupi, aby go znaleźć ;-)Aby zrobić to samo bez interfejsów dla szeregu funkcji:
źródło
Sprawdź lambdaj
http://code.google.com/p/lambdaj/
a w szczególności jego nowa funkcja zamykania
http://code.google.com/p/lambdaj/wiki/Closures
i znajdziesz bardzo czytelny sposób definiowania wskaźnika zamknięcia lub wskaźnika funkcji bez tworzenia bezsensownego interfejsu lub używania brzydkich wewnętrznych klas
źródło
Wow, dlaczego po prostu nie stworzyć klasy Delegate, która nie jest wcale taka trudna, biorąc pod uwagę, że już to zrobiłem dla java i użyć jej do przekazania parametru, w którym T jest typem zwracanym. Przykro mi, ale jako programista C ++ / C #, ogólnie po prostu uczący się języka Java, potrzebuję wskaźników funkcji, ponieważ są one bardzo przydatne. Jeśli znasz dowolną klasę, która zajmuje się informacjami o metodzie, możesz to zrobić. W bibliotekach Java byłoby to java.lang.reflect.method.
Jeśli zawsze korzystasz z interfejsu, zawsze musisz go zaimplementować. W obsłudze zdarzeń naprawdę nie ma lepszego sposobu na rejestrację / wyrejestrowanie z listy programów obsługi, ale dla delegatów, w których należy przekazać funkcje, a nie typ wartości, tworząc klasę delegata do obsługi jej w celu zdeklasowania interfejsu.
źródło
Żadna z odpowiedzi Java 8 nie dała pełnego, spójnego przykładu, więc oto nadchodzi.
Zadeklaruj metodę, która akceptuje „wskaźnik funkcji” w następujący sposób:
Wywołaj go, podając funkcję z wyrażeniem lambda:
źródło
Jeśli ktoś próbuje przekazać funkcję, która wymaga jednego zestawu parametrów do zdefiniowania jego zachowania, ale innego zestawu parametrów, na których ma zostać wykonane, na przykład Scheme:
patrz Funkcja przekazywania z zachowaniem zdefiniowanym parametrem w Javie
źródło
Od wersji Java8 możesz używać lambdas, które również mają biblioteki w oficjalnym API SE 8.
Zastosowanie: Musisz użyć interfejsu z tylko jedną metodą abstrakcyjną. Zrób jego instancję (możesz użyć już dostarczonej wersji java SE 8) w następujący sposób:
Aby uzyskać więcej informacji sprawdź dokumentację: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
źródło
Przed Javą 8 najbliższym substytutem funkcji przypominającej wskaźnik funkcji była klasa anonimowa. Na przykład:
Ale teraz w Javie 8 mamy bardzo zgrabną alternatywę znaną jako wyrażenie lambda , której można użyć jako:
gdzie isBiggerThan jest metodą w
CustomClass
. Możemy również użyć referencji metod tutaj:źródło