В нашем проекте мы переходим на java 8 и тестируем его новые функции.
В моем проекте я использую предикаты и функции Guava для фильтрации и преобразования некоторых коллекций с помощью Collections2.transform
и Collections2.filter
.
В этой миграции мне нужно изменить, например, код guava на изменения java 8. Итак, я делаю следующие изменения:
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());
Используя гуаву, мне было очень удобно отлаживать код, так как я мог отлаживать каждый процесс преобразования, но меня беспокоит, например, как отлаживать .map(n -> n*2)
.
Используя отладчик, я вижу код вроде:
@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];
}
Но отладить код не так просто, как с помощью Guava, на самом деле я не смог найти n * 2
преобразование.
Есть ли способ увидеть это преобразование или способ легко отладить этот код?
РЕДАКТИРОВАТЬ: я добавил ответ из разных комментариев и опубликовал ответы
Благодаря Holger
комментарию, который ответил на мой вопрос, подход с использованием лямбда-блока позволил мне увидеть процесс преобразования и отладить то, что произошло внутри тела лямбда:
.map(
n -> {
Integer nr = n * 2;
return nr;
}
)
Благодаря такому Stuart Marks
подходу ссылки на методы также позволили мне отладить процесс преобразования:
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Благодаря Marlon Bernardes
ответу я заметил, что мой Eclipse не показывает то, что должен, и использование peek () помогло отобразить результаты.
result
переменную какInteger
. Простое решениеint
также должно подойти, если вы отправляетеmap
пингint
наint
…Ответы:
Обычно у меня нет проблем с отладкой лямбда-выражений при использовании Eclipse или IntelliJ IDEA. Просто установите точку останова и не проверяйте все лямбда-выражение (проверяйте только тело лямбда).
Другой подход - использовать
peek
для проверки элементов потока:ОБНОВИТЬ:
Я думаю, вы запутались, потому что
map
этоintermediate operation
- другими словами: это ленивая операция, которая будет выполнена только после того, какterminal operation
был выполнен. Поэтому при вызовеstream.map(n -> n * 2)
лямбда-тела в данный момент не выполняется. Вам необходимо установить точку останова и проверить ее после вызова терминальной операции (collect
в данном случае).Проверка операций Потока для дальнейших объяснений.
ОБНОВЛЕНИЕ 2:
Цитируя комментарий Хольгера :
источник
inspect
иdisplay
и получитьn cannot be resolved to a variable
. Кстати, peek тоже полезен, но выводит все значения сразу. Я хочу видеть каждую итерацию, чтобы проверить преобразование. Возможно ли это?.map
строке и нажмите F8 несколько раз.map
и лямбда-выражение находятся в одной строке, поэтому точка останова на строке остановится на двух совершенно не связанных действиях. Вставка разрыва строки сразу послеmap(
позволит вам установить точку останова только для лямбда-выражения. И нет ничего необычного в том, что отладчики не показывают промежуточные значенияreturn
оператора. Изменение лямбды наn -> { int result=n * 2; return result; }
позволит вам проверитьresult
. Опять же, вставляйте разрывы строк соответствующим образом при переходе по строкам…У IntelliJ есть такой хороший плагин для этого случая, как плагин Java Stream Debugger . Вы должны проверить это: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite
Он расширяет окно инструмента IDEA Debugger, добавляя кнопку Trace Current Stream Chain, которая становится активной, когда отладчик останавливается внутри цепочки вызовов Stream API.
Он имеет приятный интерфейс для работы с отдельными потоками операций и дает вам возможность следить за некоторыми значениями, которые вам следует отладить.
Вы можете запустить его вручную из окна отладки, щелкнув здесь:
источник
Отладка лямбда-выражений также хорошо работает с NetBeans. Я использую NetBeans 8 и JDK 8u5.
Если вы установите точку останова в строке, где есть лямбда, вы действительно попадете один раз при настройке конвейера, а затем один раз для каждого элемента потока. Используя ваш пример, при первом попадании в точку останова будет
map()
вызов, который настраивает конвейер потока:Вы можете увидеть стек вызовов, а также локальные переменные и значения параметров,
main
как и следовало ожидать. Если вы продолжите пошаговое выполнение, «та же» точка останова будет достигнута снова, за исключением того, что на этот раз она находится внутри вызова лямбда:Обратите внимание, что на этот раз стек вызовов находится глубоко внутри механизма потоков, а локальные переменные - это локальные переменные самой лямбды, а не включающий
main
метод. (Я изменил значения вnaturals
списке, чтобы прояснить это.)Как отметил Марлон Бернардес (+1), вы можете использовать его
peek
для проверки значений по мере их прохождения в конвейере. Однако будьте осторожны, если вы используете это из параллельного потока. Значения могут быть напечатаны в непредсказуемом порядке в разных потоках. Если вы храните значения в структуре данных отладки изpeek
, эта структура данных, конечно, должна быть потокобезопасной.Наконец, если вы много отлаживаете лямбда-выражения (особенно лямбда-выражения с многострочными операторами), может быть предпочтительнее извлечь лямбда-выражение в именованный метод, а затем обратиться к нему, используя ссылку на метод. Например,
Это может упростить просмотр того, что происходит во время отладки. Кроме того, такой способ извлечения методов упрощает модульное тестирование. Если ваша лямбда настолько сложна, что вам нужно выполнять ее пошагово, вы, вероятно, все равно захотите провести для нее несколько модульных тестов.
источник
{ int result=n * 2; return result; }
разные строки, и я мог принять ответ, поскольку оба ответа были полезны. +1 конечно.Intellij IDEA 15, кажется, делает это еще проще, он позволяет останавливаться в части строки, где находится лямбда, см. Первую функцию: http://blog.jetbrains.com/idea/2015/06/intellij-idea-15 -eap-is-open /
источник
Чтобы предоставить более подробную информацию (октябрь 2019 г.), IntelliJ добавил довольно хорошую интеграцию для отладки этого типа кода, который чрезвычайно полезен.
Когда мы останавливаемся на строке, содержащей лямбду, если мы нажимаем F7(шаг в), тогда IntelliJ выделяет фрагмент, который нужно отлаживать. Мы можем переключить блок для отладки, Tabи как только мы это решим, мы F7снова щелкнем.
Вот несколько скриншотов для иллюстрации:
1- Нажмите кнопку F7(перейти), отобразятся основные моменты (или режим выбора)
2- Используйте Tabнесколько раз, чтобы выбрать фрагмент для отладки
3- Нажмите кнопку F7(шаг в), чтобы войти в
источник
Отладка с использованием IDE всегда полезна, но идеальный способ отладки каждого элемента в потоке - использовать peek () перед операцией метода терминала, поскольку Java Steams лениво оценивается, поэтому, если не вызывается метод терминала, соответствующий поток будет не подлежат оценке.
источник