Dans notre projet, nous migrons vers java 8 et nous testons ses nouvelles fonctionnalités.
Sur mon projet, j'utilise des prédicats et des fonctions Guava pour filtrer et transformer certaines collections en utilisant Collections2.transform
et Collections2.filter
.
Sur cette migration, j'ai besoin de changer par exemple le code goyave en changements java 8. Donc, les changements que je fais sont du genre:
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);
À...
List<Integer> result2 = naturals.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
En utilisant guava, j'étais très à l'aise pour déboguer le code car je pouvais déboguer chaque processus de transformation, mais mon souci est de savoir comment déboguer par exemple .map(n -> n*2)
.
En utilisant le débogueur, je peux voir du code comme:
@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];
}
Mais ce n'est pas aussi simple que Guava de déboguer le code, en fait je n'ai pas trouvé la n * 2
transformation.
Existe-t-il un moyen de voir cette transformation ou un moyen de déboguer facilement ce code?
EDIT: J'ai ajouté une réponse à partir de différents commentaires et des réponses publiées
Merci au Holger
commentaire qui a répondu à ma question, l'approche d'avoir un bloc lambda m'a permis de voir le processus de transformation et de déboguer ce qui s'est passé à l'intérieur du corps lambda:
.map(
n -> {
Integer nr = n * 2;
return nr;
}
)
Grâce à Stuart Marks
l'approche d'avoir des références de méthode m'a également permis de déboguer le processus de transformation:
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Grâce à la Marlon Bernardes
réponse, j'ai remarqué que mon Eclipse ne montre pas ce qu'il devrait et l'utilisation de peek () a aidé à afficher les résultats.
result
variable temporaire commeInteger
. Un simpleint
devrait faire aussi bien si vous envoyezmap
un pingint
à unint
…Réponses:
Je n'ai généralement aucun problème à déboguer les expressions lambda tout en utilisant Eclipse ou IntelliJ IDEA. Définissez simplement un point d'arrêt et assurez-vous de ne pas inspecter toute l'expression lambda (inspectez uniquement le corps lambda).
Une autre approche consiste à utiliser
peek
pour inspecter les éléments du flux:METTRE À JOUR:
Je pense que vous êtes confus parce que
map
c'est unintermediate operation
- en d'autres termes: c'est une opération paresseuse qui ne sera exécutée qu'après l'terminal operation
exécution d'un. Ainsi, lorsque vous appelez,stream.map(n -> n * 2)
le corps lambda n'est pas exécuté pour le moment. Vous devez définir un point d'arrêt et l'inspecter après l'appel d'une opération de terminal (collect
dans ce cas).Consultez les opérations de flux pour plus d'explications.
MISE À JOUR 2:
Citant le commentaire de Holger :
la source
inspect
etdisplay
et d'obtenirn cannot be resolved to a variable
. Btw, peek est également utile mais imprime toutes les valeurs à la fois. Je veux voir chaque processus d'itération pour vérifier la transformation. Est-ce possible?.map
ligne et appuyez plusieurs fois sur F8.map
et l'expression lambda sont sur une seule ligne, donc un point d'arrêt de ligne s'arrêtera sur deux actions totalement indépendantes. L'insertion d'un saut de ligne juste aprèsmap(
vous permettrait de définir un point de rupture pour l'expression lambda uniquement. Et il n'est pas inhabituel que les débogueurs n'affichent pas les valeurs intermédiaires d'unereturn
instruction. Changer le lambda enn -> { int result=n * 2; return result; }
vous permettrait d'inspecterresult
. Encore une fois, insérez des sauts de ligne de manière appropriée lorsque vous avancez ligne par ligne…IntelliJ a un si bon plugin pour ce cas en tant que plugin Java Stream Debugger . Vous devriez le vérifier: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite
Il étend la fenêtre de l'outil IDEA Debugger en ajoutant le bouton Trace Current Stream Chain, qui devient actif lorsque le débogueur s'arrête à l'intérieur d'une chaîne d'appels d'API Stream.
Il a une interface agréable pour travailler avec des opérations de flux séparés et vous donne la possibilité de suivre certaines valeurs que vous devez déboguer.
Vous pouvez le lancer manuellement depuis la fenêtre de débogage en cliquant ici:
la source
Le débogage des lambdas fonctionne également bien avec NetBeans. J'utilise NetBeans 8 et JDK 8u5.
Si vous définissez un point d'arrêt sur une ligne où il y a un lambda, vous frapperez en fait une fois lorsque le pipeline est configuré, puis une fois pour chaque élément de flux. En utilisant votre exemple, la première fois que vous atteignez le point d'arrêt sera l'
map()
appel qui configure le pipeline de flux:Vous pouvez voir la pile d'appels et les variables locales et les valeurs des paramètres
main
comme prévu. Si vous continuez à avancer, le "même" point d'arrêt est à nouveau atteint, sauf que cette fois, il fait partie de l'appel au lambda:Notez que cette fois, la pile d'appels est profondément dans la machinerie des flux, et les variables locales sont les locals du lambda lui-même, pas la
main
méthode englobante . (J'ai changé les valeurs de lanaturals
liste pour que cela soit clair.)Comme Marlon Bernardes l'a souligné (+1), vous pouvez utiliser
peek
pour inspecter les valeurs au fur et à mesure qu'elles passent dans le pipeline. Soyez prudent si vous l'utilisez à partir d'un flux parallèle. Les valeurs peuvent être imprimées dans un ordre imprévisible sur différents threads. Si vous stockez des valeurs dans une structure de données de débogage à partir depeek
, cette structure de données devra bien sûr être thread-safe.Enfin, si vous effectuez beaucoup de débogage de lambdas (en particulier les lambdas d'instructions multilignes), il peut être préférable d'extraire le lambda dans une méthode nommée, puis de s'y référer à l'aide d'une référence de méthode. Par exemple,
Cela peut vous permettre de voir plus facilement ce qui se passe pendant le débogage. De plus, l'extraction des méthodes de cette manière facilite le test unitaire. Si votre lambda est si compliqué que vous devez le parcourir en une seule étape, vous voudrez probablement avoir de toute façon un tas de tests unitaires.
la source
{ int result=n * 2; return result; }
différentes lignes et je pourrais accepter la réponse car les deux réponses étaient utiles. +1 bien sûr.Intellij IDEA 15 semble le rendre encore plus facile, il permet de s'arrêter dans une partie de la ligne où se trouve lambda, voir la première fonctionnalité: http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -eap-est-ouvert /
la source
Juste pour fournir plus de détails mis à jour (octobre 2019), IntelliJ a ajouté une assez belle intégration pour déboguer ce type de code extrêmement utile.
Lorsque nous nous arrêtons à une ligne qui contient un lambda si nous appuyons sur F7(step into), IntelliJ mettra en évidence ce que sera l'extrait à déboguer. Nous pouvons changer le bloc avec lequel déboguer Tabet une fois que nous l'avons décidé, nous cliquons à F7nouveau.
Voici quelques captures d'écran pour illustrer:
1- Appuyez F7sur la touche (pas à pas), affichera les faits saillants (ou le mode de sélection)
2- Utiliser Tabplusieurs fois pour sélectionner l'extrait à déboguer
3- Appuyez F7sur la touche (step into) pour entrer
la source
Le débogage à l'aide des IDE est toujours utile, mais le moyen idéal de déboguer chaque élément d'un flux est d'utiliser peek () avant une opération de méthode de terminal, car Java Steams est évalué paresseusement, donc à moins qu'une méthode de terminal ne soit appelée, le flux respectif sera ne pas être évalué.
la source