W naszym projekcie przeprowadzamy migrację do java 8 i testujemy jej nowe funkcje.
W moim projekcie używam predykatów i funkcji Guava do filtrowania i przekształcania niektórych kolekcji przy użyciu Collections2.transform
i Collections2.filter
.
Podczas tej migracji muszę zmienić na przykład kod guava na zmiany java 8. A więc zmiany, które wprowadzam, to:
List<Integer> naturals = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10,11,12,13);
Function <Integer, Integer> duplicate = new Function<Integer, Integer>(){
@Override
public Integer apply(Integer n)
{
return n * 2;
}
};
Collection result = Collections2.transform(naturals, duplicate);
Do...
List<Integer> result2 = naturals.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
Używając guawy, czułem się bardzo komfortowo debugując kod, ponieważ mogłem debugować każdy proces transformacji, ale martwię się, jak na przykład debugować .map(n -> n*2)
.
Używając debuggera, widzę kod, taki jak:
@Hidden
@DontInline
/** Interpretively invoke this form on the given arguments. */
Object interpretWithArguments(Object... argumentValues) throws Throwable {
if (TRACE_INTERPRETER)
return interpretWithArgumentsTracing(argumentValues);
checkInvocationCounter();
assert(arityCheck(argumentValues));
Object[] values = Arrays.copyOf(argumentValues, names.length);
for (int i = argumentValues.length; i < values.length; i++) {
values[i] = interpretName(names[i], values);
}
return (result < 0) ? null : values[result];
}
Ale debugowanie kodu nie jest tak proste jak guawa, właściwie nie mogłem znaleźć n * 2
transformacji.
Czy istnieje sposób, aby zobaczyć tę transformację lub sposób na łatwe debugowanie tego kodu?
EDYCJA: Dodałem odpowiedź z różnych komentarzy i opublikowanych odpowiedzi
Dzięki Holger
komentarzowi, który odpowiedział na moje pytanie, podejście z blokiem lambda pozwoliło mi zobaczyć proces transformacji i debugować, co się stało wewnątrz ciała lambda:
.map(
n -> {
Integer nr = n * 2;
return nr;
}
)
Dzięki Stuart Marks
podejściu posiadania referencji do metod umożliwiło mi również debugowanie procesu transformacji:
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Dzięki Marlon Bernardes
odpowiedzi zauważyłem, że moje Eclipse nie pokazuje, co powinno, a użycie peek () pomogło wyświetlić wyniki.
result
zmiennej jakoInteger
. Prosty teżint
powinien wystarczyć, jeślimap
wysyłasz pingint
doint
…Odpowiedzi:
Zwykle nie mam problemu z debugowaniem wyrażeń lambda podczas korzystania z Eclipse lub IntelliJ IDEA. Po prostu ustaw punkt przerwania i nie sprawdzaj całego wyrażenia lambda (sprawdzaj tylko treść lambda).
Innym podejściem jest użycie
peek
do inspekcji elementów strumienia:AKTUALIZACJA:
Myślę, że jesteś zdezorientowany, ponieważ
map
jest tointermediate operation
- innymi słowy: jest to leniwa operacja, która zostanie wykonana dopiero poterminal operation
wykonaniu a. Więc kiedy wywołujeszstream.map(n -> n * 2)
ciało lambda, w tej chwili nie jest wykonywane. Musisz ustawić punkt przerwania i sprawdzić go po wywołaniu operacji terminalowej (collect
w tym przypadku).Sprawdź Operacje na strumieniu, aby uzyskać dalsze wyjaśnienia.
AKTUALIZACJA 2:
Cytując komentarz Holgera :
źródło
inspect
idisplay
i dostaćn cannot be resolved to a variable
. Przy okazji, peek też jest przydatny, ale wyświetla wszystkie wartości naraz. Chcę zobaczyć każdy proces iteracji, aby sprawdzić transformację. Czy to możliwe?.map
linii i naciśnij kilka razy klawisz F8.map
i wyrażenie lambda znajdują się w jednej linii, więc punkt przerwania linii zatrzyma się na dwóch zupełnie niezwiązanych ze sobą akcjach. Wstawieniemap(
końca wiersza zaraz po nim pozwoliłoby ustawić punkt przerwania tylko dla wyrażenia lambda. I nie jest niczym niezwykłym, że debuggery nie pokazują pośrednich wartościreturn
instrukcji. Zmiana lambda nan -> { int result=n * 2; return result; }
umożliwiłaby sprawdzenieresult
. Ponownie wstaw odpowiednio znaki końca wiersza, przechodząc wiersz po wierszu…IntelliJ ma tak fajną wtyczkę do tego przypadku, jak wtyczka Java Stream Debugger . Powinieneś to sprawdzić: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite
Rozszerza okno narzędzia IDEA Debugger, dodając przycisk Śledź bieżący łańcuch strumienia, który staje się aktywny, gdy debugger zatrzymuje się w łańcuchu wywołań interfejsu API Stream.
Ma ładny interfejs do pracy z oddzielnymi operacjami na strumieniach i daje możliwość śledzenia niektórych wartości, które należy debugować.
Możesz uruchomić go ręcznie z okna Debug, klikając tutaj:
źródło
Debugowanie lambd działa również dobrze z NetBeans. Używam NetBeans 8 i JDK 8u5.
Jeśli ustawisz punkt przerwania w linii, w której znajduje się lambda, faktycznie trafisz raz podczas konfigurowania potoku, a następnie raz dla każdego elementu strumienia. Korzystając z naszego przykładu, pierwszym uderzeniem w punkt przerwania będzie
map()
wywołanie, które konfiguruje potok strumienia:Możesz zobaczyć stos wywołań oraz zmienne lokalne i wartości parametrów
main
zgodnie z oczekiwaniami. Jeśli będziesz kontynuować przechodzenie po krokach, „ten sam” punkt przerwania zostanie ponownie trafiony, z wyjątkiem tego, że tym razem znajduje się on w wywołaniu lambda:Zauważ, że tym razem stos wywołań znajduje się głęboko w maszynerii strumieni, a zmienne lokalne są lokalnymi zmiennymi samej lambda, a nie otaczającą
main
metodą. (Zmieniłem wartości nanaturals
liście, aby to wyjaśnić).Jak zauważył Marlon Bernardes (+1), możesz używać
peek
do sprawdzania wartości w trakcie ich przebiegu. Uważaj jednak, jeśli używasz tego z równoległego strumienia. Wartości mogą być drukowane w nieprzewidywalnej kolejności w różnych wątkach. Jeśli przechowujesz wartości w strukturze danych debugowania zpeek
, ta struktura danych będzie oczywiście musiała być bezpieczna wątkowo.Na koniec, jeśli robisz dużo debugowania lambd (szczególnie wyrażeń wieloliniowych), może być lepiej wyodrębnić lambdę do nazwanej metody, a następnie odwołać się do niej za pomocą odwołania do metody. Na przykład,
Może to ułatwić sprawdzenie, co się dzieje podczas debugowania. Ponadto wyodrębnianie metod w ten sposób ułatwia testowanie jednostkowe. Jeśli twoja lambda jest tak skomplikowana, że musisz przejść przez nią jeden krok, prawdopodobnie i tak chcesz mieć dla niej kilka testów jednostkowych.
źródło
{ int result=n * 2; return result; }
różne linie i mogłem zaakceptować odpowiedź, ponieważ obie odpowiedzi były pomocne. +1 oczywiście.Intellij IDEA 15 wydaje się jeszcze to ułatwiać, pozwala zatrzymać się w części linii, w której jest lambda, zobacz pierwszą cechę: http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -eap-is-open /
źródło
Aby zapewnić bardziej aktualne szczegóły (październik 2019 r.), IntelliJ dodał całkiem niezłą integrację do debugowania tego typu kodu, który jest niezwykle przydatny.
Kiedy zatrzymamy się na linii zawierającej lambdę, jeśli naciśniemy F7(step into), wówczas IntelliJ podświetli, co będzie fragmentem do debugowania. Możemy zmienić fragment do debugowania, Taba kiedy już zdecydujemy, klikamy F7ponownie.
Oto kilka zrzutów ekranu do zilustrowania:
1- Naciśnij przycisk F7(wejdź do), wyświetli się podświetlenie (lub tryb wyboru)
2- Użyj Tabwiele razy, aby wybrać fragment do debugowania
3- Naciśnij przycisk F7(step into), aby wejść
źródło
Debugowanie przy użyciu IDE jest zawsze pomocne, ale idealnym sposobem debugowania przez każdy element w strumieniu jest użycie funkcji peek () przed operacją metody terminalowej, ponieważ Java Steams są leniwie oceniane, więc jeśli nie zostanie wywołana metoda terminalowa, odpowiedni strumień będzie nie podlegać ocenie.
źródło