Em nosso projeto, estamos migrando para o java 8 e testando os novos recursos dele.
No meu projeto estou usando predicados e funções de Guava para filtrar e transformar algumas coleções usando Collections2.transform
e Collections2.filter
.
Nesta migração eu preciso mudar, por exemplo, o código de goiaba para java 8 mudanças. Então, as mudanças que estou fazendo são do tipo:
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);
Para...
List<Integer> result2 = naturals.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
Usando o goiaba fiquei muito confortável depurando o código, já que poderia depurar cada processo de transformação, mas minha preocupação é como depurar, por exemplo .map(n -> n*2)
.
Usando o depurador, posso ver alguns códigos como:
@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];
}
Mas não é tão simples quanto Guava depurar o código, na verdade não consegui encontrar a n * 2
transformação.
Existe uma maneira de ver essa transformação ou uma maneira de depurar facilmente esse código?
EDITAR: Adicionei respostas de diferentes comentários e postei respostas
Graças ao Holger
comentário que respondeu à minha pergunta, a abordagem de ter o bloco lambda me permitiu ver o processo de transformação e depurar o que aconteceu dentro do corpo lambda:
.map(
n -> {
Integer nr = n * 2;
return nr;
}
)
Graças à Stuart Marks
abordagem de ter referências de método também me permitiu depurar o processo de transformação:
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Graças à Marlon Bernardes
resposta, percebi que meu Eclipse não mostra o que deveria e o uso de peek () ajudou a exibir os resultados.
result
variável temporária comoInteger
. Um simplesint
deve servir também se você estivermap
executando pingint
em umint
...Respostas:
Normalmente não tenho problemas para depurar expressões lambda ao usar Eclipse ou IntelliJ IDEA. Basta definir um ponto de interrupção e não inspecionar a expressão lambda inteira (inspecione apenas o corpo lambda).
Outra abordagem é usar
peek
para inspecionar os elementos do fluxo:ATUALIZAR:
Acho que você está ficando confuso porque
map
é umaintermediate operation
- em outras palavras: é uma operação preguiçosa que só será executada depois que aterminal operation
for executada. Então, quando você chama,stream.map(n -> n * 2)
o corpo lambda não está sendo executado no momento. Você precisa definir um ponto de interrupção e inspecioná-lo depois que uma operação de terminal foi chamada (collect
neste caso).Verifique o Stream Operations para mais explicações.
ATUALIZAÇÃO 2:
Citando o comentário de Holger :
fonte
inspect
anddisplay
and getn cannot be resolved to a variable
. Aliás, peek também é útil, mas imprime todos os valores de uma vez. Quero ver cada processo de iteração para verificar a transformação. É possível?.map
line e pressionar F8 várias vezes.map
e a expressão lambda estão em uma linha, então um ponto de interrupção de linha irá parar em duas ações completamente não relacionadas. Inserir uma quebra de linha logo apósmap(
permitiria definir um ponto de interrupção apenas para a expressão lambda. E não é incomum que os depuradores não mostrem valores intermediários de umareturn
instrução. Alterar o lambda paran -> { int result=n * 2; return result; }
permitiria que você inspecionasseresult
. Mais uma vez, insira quebras de linha apropriadamente ao avançar linha por linha ...O IntelliJ tem um plugin tão bom para este caso como um plugin Java Stream Debugger . Você deve conferir: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite
Ele estende a janela da ferramenta IDEA Debugger adicionando o botão Trace Current Stream Chain, que se torna ativo quando o depurador para dentro de uma cadeia de chamadas de API de fluxo.
Ele tem uma interface agradável para trabalhar com operações de fluxos separados e dá a você a oportunidade de seguir alguns valores que você deve depurar.
Você pode iniciá-lo manualmente na janela de depuração clicando aqui:
fonte
A depuração de lambdas também funciona bem com o NetBeans. Estou usando o NetBeans 8 e o JDK 8u5.
Se você definir um ponto de interrupção em uma linha onde há um lambda, você realmente atingirá uma vez quando o pipeline for configurado e, em seguida, uma vez para cada elemento de fluxo. Usando seu exemplo, a primeira vez que você atingir o ponto de interrupção será a
map()
chamada que está configurando o pipeline de fluxo:Você pode ver a pilha de chamadas e as variáveis locais e os valores dos parâmetros
main
conforme o esperado. Se você continuar avançando, o "mesmo" ponto de interrupção será atingido novamente, mas desta vez dentro da chamada ao lambda:Observe que, desta vez, a pilha de chamadas está profundamente dentro do mecanismo dos streams e as variáveis locais são os locais do próprio lambda, não o
main
método envolvente . (Eu mudei os valores nanaturals
lista para deixar isso claro.)Como Marlon Bernardes apontou (+1), você pode usar
peek
para inspecionar valores à medida que eles passam no pipeline. Porém, tenha cuidado se estiver usando isso de um fluxo paralelo. Os valores podem ser impressos em uma ordem imprevisível em diferentes threads. Se você estiver armazenando valores em uma estrutura de dados de depuraçãopeek
, essa estrutura de dados terá que ser segura para threads.Por fim, se você estiver depurando muito os lambdas (especialmente lambdas de instruções de várias linhas), pode ser preferível extrair o lambda em um método nomeado e, em seguida, referir-se a ele usando uma referência de método. Por exemplo,
Isso pode tornar mais fácil ver o que está acontecendo durante a depuração. Além disso, extrair métodos dessa maneira facilita o teste de unidade. Se o seu lambda é tão complicado que você precisa executá-lo em uma única etapa, você provavelmente deseja ter um monte de testes de unidade para ele de qualquer maneira.
fonte
{ int result=n * 2; return result; }
linhas diferentes e eu poderia aceitar a resposta, pois ambas as respostas foram úteis. 1 claro.Intellij IDEA 15 parece tornar ainda mais fácil, pois permite parar em uma parte da linha onde está lambda, veja o primeiro recurso: http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -eap-is-open /
fonte
Apenas para fornecer detalhes mais atualizados (outubro de 2019), o IntelliJ adicionou uma boa integração para depurar esse tipo de código que é extremamente útil.
Quando paramos em uma linha que contém um lambda, se pressionarmos F7(entrar em), o IntelliJ destacará qual será o trecho a ser depurado. Podemos mudar o bloco para depurar Tabe, uma vez que decidimos, clicamos F7novamente.
Aqui estão algumas imagens para ilustrar:
1- Pressione a tecla F7(entrar) para exibir os destaques (ou modo de seleção)
2- Use Tabvárias vezes para selecionar o snippet para depurar
3- Pressione a tecla F7(entrar) para entrar
fonte
Depurar usando IDE é sempre útil, mas a maneira ideal de depurar por meio de cada elemento em um fluxo é usar peek () antes de uma operação de método de terminal, pois os Java Steams são avaliados lentamente, então, a menos que um método de terminal seja invocado, o respectivo fluxo não ser avaliado.
fonte