In unserem Projekt migrieren wir auf Java 8 und testen die neuen Funktionen.
In meinem Projekt verwende ich Guava-Prädikate und -Funktionen, um einige Sammlungen mit Collections2.transform
und zu filtern und zu transformieren Collections2.filter
.
Bei dieser Migration muss ich zum Beispiel Guavencode in Java 8 ändern. Die Änderungen, die ich vornehme, sind folgende:
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);
Zu...
List<Integer> result2 = naturals.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
Mit Guave war ich sehr komfortabel beim Debuggen des Codes, da ich jeden Transformationsprozess debuggen konnte, aber mein Anliegen ist beispielsweise das Debuggen .map(n -> n*2)
.
Mit dem Debugger kann ich folgenden Code sehen:
@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];
}
Aber es ist nicht so einfach wie Guava, den Code zu debuggen, eigentlich konnte ich die n * 2
Transformation nicht finden .
Gibt es eine Möglichkeit, diese Transformation zu sehen oder diesen Code einfach zu debuggen?
BEARBEITEN: Ich habe Antworten aus verschiedenen Kommentaren hinzugefügt und Antworten gepostet
Dank eines Holger
Kommentars, der meine Frage beantwortete, ermöglichte mir der Ansatz, einen Lambda-Block zu haben, den Transformationsprozess zu sehen und zu debuggen, was im Lambda-Körper passiert ist:
.map(
n -> {
Integer nr = n * 2;
return nr;
}
)
Dank Stuart Marks
des Ansatzes, Methodenreferenzen zu haben, konnte ich auch den Transformationsprozess debuggen:
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Dank der Marlon Bernardes
Antwort bemerkte ich, dass meine Eclipse nicht anzeigt, was sie sollte, und die Verwendung von peek () half, Ergebnisse anzuzeigen.
result
Variable nicht als deklarierenInteger
. Eine einfacheint
sollten auch tun , wenn Sie sindmap
Ping einint
zu einemint
...Antworten:
Normalerweise habe ich kein Problem damit, Lambda-Ausdrücke zu debuggen, während ich Eclipse oder IntelliJ IDEA verwende. Setzen Sie einfach einen Haltepunkt und achten Sie darauf, nicht den gesamten Lambda-Ausdruck zu überprüfen (überprüfen Sie nur den Lambda-Körper).
Ein anderer Ansatz besteht darin
peek
, die Elemente des Streams zu untersuchen:AKTUALISIEREN:
Ich denke, Sie werden verwirrt, weil
map
es sich um eineintermediate operation
- mit anderen Worten: es handelt sich um eine faule Operation handelt, die erst ausgeführt wird, nachdem aterminal operation
ausgeführt wurde. Wenn Sie alsostream.map(n -> n * 2)
den Lambda-Körper anrufen , wird er im Moment nicht ausgeführt. Sie müssen einen Haltepunkt festlegen und ihn überprüfen, nachdem eine Terminaloperation aufgerufen wurde (collect
in diesem Fall).Überprüfen Sie Stream Operations auf weitere Erklärungen.
UPDATE 2:
Zitat von Holgers Kommentar:
quelle
inspect
unddisplay
und zu bekommenn cannot be resolved to a variable
. Übrigens ist Peek auch nützlich, druckt aber alle Werte auf einmal. Ich möchte jeden Iterationsprozess sehen, um die Transformation zu überprüfen. Ist es möglich.map
online und drücken Sie mehrmals F8.map
und der Lambda-Ausdruck in einer Zeile stehen, sodass ein Zeilenumbruchpunkt bei zwei völlig unabhängigen Aktionen stoppt. Wenn Sie direkt danach einen Zeilenumbruch einfügenmap(
, können Sie einen Unterbrechungspunkt nur für den Lambda-Ausdruck festlegen. Und es ist nicht ungewöhnlich, dass Debugger keine Zwischenwerte einerreturn
Anweisung anzeigen. Wenn Sie das Lambda ändern,n -> { int result=n * 2; return result; }
können Sie es überprüfenresult
. Fügen Sie erneut Zeilenumbrüche entsprechend ein, wenn Sie Zeile für ZeileIntelliJ hat für diesen Fall ein so schönes Plugin wie ein Java Stream Debugger- Plugin. Sie sollten es überprüfen: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite
Es erweitert das IDEA Debugger-Toolfenster durch Hinzufügen der Schaltfläche Aktuelle Stream-Kette verfolgen, die aktiv wird, wenn der Debugger innerhalb einer Kette von Stream-API-Aufrufen stoppt.
Es hat eine schöne Oberfläche für die Arbeit mit separaten Streams und bietet Ihnen die Möglichkeit, einige Werte zu befolgen, die Sie debuggen sollten.
Sie können es manuell über das Debug-Fenster starten, indem Sie hier klicken:
quelle
Das Debuggen von Lambdas funktioniert auch gut mit NetBeans. Ich verwende NetBeans 8 und JDK 8u5.
Wenn Sie einen Haltepunkt in einer Zeile festlegen, in der sich ein Lambda befindet, werden Sie beim Einrichten der Pipeline tatsächlich einmal und dann für jedes Stream-Element einmal getroffen. In Ihrem Beispiel ist das erste Mal, dass Sie den Haltepunkt erreichen, der
map()
Aufruf, der die Stream-Pipeline einrichtet:Sie können den Aufrufstapel sowie die lokalen Variablen und Parameterwerte
main
wie erwartet anzeigen. Wenn Sie weitergehen, wird der "gleiche" Haltepunkt erneut erreicht, außer diesmal innerhalb des Aufrufs an das Lambda:Beachten Sie, dass sich der Aufrufstapel diesmal tief in der Streams-Maschinerie befindet und die lokalen Variablen die Einheimischen des Lambda selbst sind, nicht die einschließende
main
Methode. (Ich habe die Werte in dernaturals
Liste geändert , um dies zu verdeutlichen.)Wie Marlon Bernardes betonte (+1), können Sie damit
peek
Werte überprüfen, die in der Pipeline vergehen. Seien Sie jedoch vorsichtig, wenn Sie dies aus einem parallelen Stream verwenden. Die Werte können in einer unvorhersehbaren Reihenfolge über verschiedene Threads hinweg gedruckt werden. Wenn Sie Werte in einer Debugging-Datenstruktur aus speichernpeek
, muss diese Datenstruktur natürlich threadsicher sein.Wenn Sie Lambdas häufig debuggen (insbesondere Lambdas mit mehrzeiligen Anweisungen), ist es möglicherweise vorzuziehen, das Lambda in eine benannte Methode zu extrahieren und dann mithilfe einer Methodenreferenz darauf zu verweisen. Beispielsweise,
Dies macht es möglicherweise einfacher zu sehen, was beim Debuggen vor sich geht. Darüber hinaus erleichtert das Extrahieren von Methoden auf diese Weise den Unit-Test. Wenn Ihr Lambda so kompliziert ist, dass Sie es in einem Schritt durchlaufen müssen, möchten Sie wahrscheinlich trotzdem eine Reihe von Unit-Tests dafür durchführen.
quelle
{ int result=n * 2; return result; }
verschiedene Zeilen hinzufügen , und ich kann die Antwort akzeptieren, da beide Antworten hilfreich sind. +1 natürlich.Intellij IDEA 15 scheint es noch einfacher zu machen, es ermöglicht das Anhalten in einem Teil der Linie, in der sich Lambda befindet, siehe die erste Funktion: http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -eap-is-open /
quelle
Um weitere aktualisierte Details bereitzustellen (Oktober 2019), hat IntelliJ eine hübsche Integration hinzugefügt, um diese Art von Code zu debuggen, die äußerst nützlich ist.
Wenn wir an einer Zeile anhalten, die ein Lambda enthält, wenn wir drücken F7(eintreten), hebt IntelliJ das zu debuggende Snippet hervor. Wir können wechseln, mit welchem Block Tabwir debuggen möchten, und sobald wir uns dafür entschieden haben, klicken wir F7erneut.
Hier einige Screenshots zur Veranschaulichung:
1- Drücken Sie die F7Taste (Schritt in), um die Markierungen (oder den Auswahlmodus) anzuzeigen.
2- Verwenden Tab Sie das zu debuggende Snippet mehrmals aus
3- Drücken Sie die F7Taste (Schritt hinein), um hineinzugehen
quelle
Das Debuggen mit IDEs ist immer hilfreich, aber die ideale Methode zum Debuggen durch die einzelnen Elemente in einem Stream besteht darin, peek () vor einer Terminalmethodenoperation zu verwenden, da Java-Steams träge ausgewertet werden. Wenn also keine Terminalmethode aufgerufen wird, wird der jeweilige Stream verwendet nicht ausgewertet werden.
quelle