Bawię się Javą 8, aby dowiedzieć się, jak funkcjonuje jako obywatel pierwszej klasy. Mam następujący fragment:
package test;
import java.util.*;
import java.util.function.*;
public class Test {
public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
list.forEach(functionToBlock(myFunction));
}
public static void displayInt(Integer i) {
System.out.println(i);
}
public static void main(String[] args) {
List<Integer> theList = new ArrayList<>();
theList.add(1);
theList.add(2);
theList.add(3);
theList.add(4);
theList.add(5);
theList.add(6);
myForEach(theList, Test::displayInt);
}
}
To, co próbuję zrobić, to przekazać metodę displayInt
do metody myForEach
przy użyciu odwołania do metody. Kompilator generuje następujący błąd:
src/test/Test.java:9: error: cannot find symbol
list.forEach(functionToBlock(myFunction));
^
symbol: method functionToBlock(Function<Integer,Void>)
location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
myForEach(theList, Test::displayInt);
^
required: List<Integer>,Function<Integer,Void>
found: List<Integer>,Test::displayInt
reason: argument mismatch; bad return type in method reference
void cannot be converted to Void
Kompilator narzeka na to void cannot be converted to Void
. Nie wiem, jak określić typ interfejsu funkcji w podpisie myForEach
takiego, który kompiluje kod. Wiem, że mógłbym po prostu zmienić typ powrotu displayInt
do, Void
a następnie wrócić null
. Mogą jednak zaistnieć sytuacje, w których nie będzie można zmienić metody, którą chcę przekazać gdzie indziej. Czy istnieje łatwy sposób ponownego wykorzystania displayInt
w obecnej postaci?
Block
został zmieniony naConsumer
w najnowszych wersjach JDK 8 APIMethodHandle
), a używając tego kompilator Java jest w stanie stworzyć bardziej wydajny kod, np. Implementując lambdy bez zamykania jako metody statyczne, zapobiegając w ten sposób generowaniu zajęcia dodatkowe.Function<Void, Void>
toRunnable
.Runnable
iCallable
interfejsach jest napisane, że mają być używane w połączeniu z wątkami . Irytującą konsekwencją tego jest to, że niektóre narzędzia do analizy statycznej (takie jak Sonar) będą narzekać, jeśli wywołaszrun()
metodę bezpośrednio.Kiedy musisz przyjąć funkcję jako argument, która nie przyjmuje argumentów i nie zwraca wyniku (void), moim zdaniem nadal najlepiej jest mieć coś takiego
gdzieś w swoim kodzie. Na moich kursach programowania funkcjonalnego do opisania takich funkcji używano słowa „thunk”. Dlaczego nie ma go w java.util.function, jest poza moim zrozumieniem.
W innych przypadkach stwierdzam, że nawet wtedy, gdy java.util.function ma coś, co pasuje do podpisu, którego potrzebuję - nadal nie zawsze wydaje się właściwe, gdy nazewnictwo interfejsu nie pasuje do użycia funkcji w moim kodzie. Wydaje mi się, że jest to podobna kwestia, która pojawia się w innym miejscu tutaj w odniesieniu do „Runnable” - który jest terminem związanym z klasą Thread - więc chociaż może mieć podpis, którego potrzebuję, nadal może zmylić czytelnika.
źródło
Runnable
nie zezwala na sprawdzane wyjątki, przez co jest bezużyteczny dla wielu aplikacji. PonieważCallable<Void>
nie jest to również przydatne, wydaje się, że musisz zdefiniować własne. Brzydki.Ustaw typ zwrotu na
Void
zamiastvoid
ireturn null
LUB
źródło
Uważam, że zamiast tego powinieneś używać interfejsu klienta
Function<T, R>
.Konsument jest w zasadzie funkcjonalnym interfejsem zaprojektowanym do akceptowania wartości i niczego nie zwracającego (tj. Void)
W twoim przypadku możesz utworzyć konsumenta w innym miejscu kodu w ten sposób:
Następnie możesz zamienić
myForEach
kod na poniższy fragment:Traktujesz myFunction jako obiekt pierwszej klasy.
źródło