Java 8: форматирование лямбда с новыми строками и отступами

С помощью лямбда-отступа я хотел бы добиться следующего:

Многострочное заявление:

String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" };
List<String> strings = Arrays.stream(ppl)
                         .filter(
                             (x) ->
                             {
                                 return x.contains("(M)");
                             }
                         ).collect(Collectors.toList());
strings.stream().forEach(System.out::println);

Однострочное заявление:

List<String> strings = Arrays.stream(ppl)
                         .map((x) -> x.toUpperCase())
                         .filter((x) -> x.contains("(M)"))
                         .collect(Collectors.toList());



В настоящее время Eclipse выполняет автоматическое форматирование следующим образом:

Многострочное заявление:

String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" };
List<String> strings = Arrays.stream(ppl).filter((x) ->
{
    return x.contains("(M)");
}).collect(Collectors.toList());
strings.stream().forEach(System.out::println);

Однострочное заявление:

String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des(M)", "Rick (M)" };
List<String> strings = Arrays.stream(ppl).map((x) -> x.toUpperCase())
        .filter((x) -> x.contains("(M)")).collect(Collectors.toList());
strings.stream().forEach(System.out::println);

И я нахожу это действительно беспорядочным, потому что вызов collect находится прямо под return, и между ними вообще нет места. Я бы предпочел, чтобы я мог начать лямбду в новой строке с отступом, и чтобы вызов .filter( был прямо над вызовом .collect(. Однако единственное, что можно настроить с помощью стандартного Java-8 Eclipse Formatter, — это фигурную скобку в начале тела лямбда-выражения, но ничего заранее для скобок () и отступа.

А в случае однострочных вызовов он просто использует базовый перенос строк и превращает его в цепочку беспорядка. Я не думаю, что мне нужно объяснять, почему это трудно расшифровать впоследствии.

Есть ли способ как-то больше настроить форматирование и добиться первого типа форматирования в Eclipse? (Или, при желании, в другой IDE, такой как IntelliJ IDEA.)



РЕДАКТИРОВАТЬ: Самое близкое, что я мог получить, было с IntelliJ IDEA 13 Community Edition (читай: бесплатная версия: P), которая была следующей (определяемой непрерывным отступом, который в данном случае равен 8):

public static void main(String[] args)
{
    int[] x = new int[] {1, 2, 3, 4, 5, 6, 7};
    int sum = Arrays.stream(x)
            .map((n) -> n * 5)
            .filter((n) -> {
                System.out.println("Filtering: " + n);
                return n % 3 != 0;
            })
            .reduce(0, Integer::sum);

    List<Integer> list = Arrays.stream(x)
            .filter((n) -> n % 2 == 0)
            .map((n) -> n * 4)
            .boxed()
            .collect(Collectors.toList());
    list.forEach(System.out::println);
    System.out.println(sum);

Это также позволяет «выровнять» вызов связанного метода следующим образом:

    int sum = Arrays.stream(x)
                    .map((n) -> n * 5)
                    .filter((n) -> {
                        System.out.println("Filtering: " + n);
                        return n % 3 != 0;
                    })
                    .reduce(0, Integer::sum);


    List<Integer> list = Arrays.stream(x)
                               .filter((n) -> n % 2 == 0)
                               .map((n) -> n * 4)
                               .boxed()
                               .collect(Collectors.toList());
    list.forEach(System.out::println);
    System.out.println(sum);
}

Лично я считаю, что, хотя вторая версия имеет больше смысла, она слишком отодвигает ее, поэтому я предпочитаю первую.

Настройка, отвечающая за первую настройку, следующая:

<?xml version="1.0" encoding="UTF-8"?>
<code_scheme name="Zhuinden">
  <option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
  <option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
  <option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
  <option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />
  <option name="JD_P_AT_EMPTY_LINES" value="false" />
  <option name="JD_PARAM_DESCRIPTION_ON_NEW_LINE" value="true" />
  <option name="WRAP_COMMENTS" value="true" />
  <codeStyleSettings language="JAVA">
    <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
    <option name="BRACE_STYLE" value="2" />
    <option name="CLASS_BRACE_STYLE" value="2" />
    <option name="METHOD_BRACE_STYLE" value="2" />
    <option name="ELSE_ON_NEW_LINE" value="true" />
    <option name="WHILE_ON_NEW_LINE" value="true" />
    <option name="CATCH_ON_NEW_LINE" value="true" />
    <option name="FINALLY_ON_NEW_LINE" value="true" />
    <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
    <option name="SPACE_WITHIN_BRACES" value="true" />
    <option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
    <option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
    <option name="METHOD_PARAMETERS_WRAP" value="1" />
    <option name="EXTENDS_LIST_WRAP" value="1" />
    <option name="THROWS_LIST_WRAP" value="1" />
    <option name="EXTENDS_KEYWORD_WRAP" value="1" />
    <option name="THROWS_KEYWORD_WRAP" value="1" />
    <option name="METHOD_CALL_CHAIN_WRAP" value="2" />
    <option name="BINARY_OPERATION_WRAP" value="1" />
    <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
    <option name="ASSIGNMENT_WRAP" value="1" />
    <option name="IF_BRACE_FORCE" value="3" />
    <option name="DOWHILE_BRACE_FORCE" value="3" />
    <option name="WHILE_BRACE_FORCE" value="3" />
    <option name="FOR_BRACE_FORCE" value="3" />
    <option name="PARAMETER_ANNOTATION_WRAP" value="1" />
    <option name="VARIABLE_ANNOTATION_WRAP" value="1" />
    <option name="ENUM_CONSTANTS_WRAP" value="2" />
  </codeStyleSettings>
</code_scheme>

Я пытался убедиться, что все в порядке, но, возможно, я что-то напутал, поэтому могут потребоваться небольшие корректировки.

Если вы венгр, как и я, и используете венгерскую раскладку, то эта раскладка может быть вам полезна, чтобы вы не оказались в состоянии использовать AltGR+F, AltGR+G, AltGR+B , AltGR+N и AltGR+M (соответствующие Ctrl+Alt).

<?xml version="1.0" encoding="UTF-8"?>
<keymap version="1" name="Default copy" parent="$default">
  <action id="ExtractMethod">
    <keyboard-shortcut first-keystroke="shift control M" />
  </action>
  <action id="GotoImplementation">
    <mouse-shortcut keystroke="control alt button1" />
  </action>
  <action id="GotoLine">
    <keyboard-shortcut first-keystroke="shift control G" />
  </action>
  <action id="Inline">
    <keyboard-shortcut first-keystroke="shift control O" />
  </action>
  <action id="IntroduceField">
    <keyboard-shortcut first-keystroke="shift control D" />
  </action>
  <action id="Mvc.RunTarget">
    <keyboard-shortcut first-keystroke="shift control P" />
  </action>
  <action id="StructuralSearchPlugin.StructuralReplaceAction" />
  <action id="Synchronize">
    <keyboard-shortcut first-keystroke="shift control Y" />
  </action>
</keymap>

Хотя IntelliJ, похоже, не дает возможности поместить открывающую скобку лямбды в новую строку, в противном случае это довольно разумный способ форматирования, поэтому я отмечу это как принятое.


person EpicPandaForce    schedule 09.07.2014    source источник
comment
Вы не можете использовать .filter(x -> x.contains("(M)"))? Гораздо проще... Если вы на самом деле говорите о местах, где вам нужно несколько утверждений, было бы лучше привести пример, в котором это необходимо.   -  person Jon Skeet    schedule 09.07.2014
comment
@JonSkeet Прямо сейчас я не мог придумать действительно хороший вариант использования многострочного режима, но вместо этого я добавил оба случая.   -  person EpicPandaForce    schedule 09.07.2014
comment
Честно говоря, версия «Netbeans 8.0», которую кто-то опубликовал ниже и удалила, была очень близка к истине, хотя Netbeans печально известен тем, что не позволяет правильно форматировать открывающие скобки операторов в стиле Allman.   -  person EpicPandaForce    schedule 09.07.2014
comment
Я предполагаю, что текущее решение позволяет никогда не соединять завернутые строки и форматировать вручную, но это кажется несколько утомительным по сравнению с нажатием Ctrl+Alt+F, если вы автоматически форматируете чужой код.   -  person EpicPandaForce    schedule 09.07.2014
comment
И по какой-то причине у Eclipse были проблемы с определением типа x без открытия и закрытия фигурной скобки, поэтому я сначала использовал оператор, а не простую версию.   -  person EpicPandaForce    schedule 09.07.2014


Ответы (8)


arrow_upward
31
arrow_downward

Из коробки IntelliJ 13, вероятно, будет работать для вас.

Если я напишу так:

// Mulit-Line Statement
String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" };
List<String> strings = Arrays.stream(ppl)
        .filter(
                (x) ->
                {
                    return x.contains("(M)");
                }
        ).collect(Collectors.toList());
strings.stream().forEach(System.out::println);

Затем примените автоформатирование (без изменений):

// Mulit-Line Statement
String[] ppl = new String[]{"Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)"};
List<String> strings = Arrays.stream(ppl)
        .filter(
                (x) ->
                {
                    return x.contains("(M)");
                }
        ).collect(Collectors.toList());
strings.stream().forEach(System.out::println);

То же самое верно и для вашего однострочного оператора. По моему опыту, IntelliJ более гибок в применении автоматического форматирования. IntelliJ с меньшей вероятностью удалит или добавит возврат строки, если вы поместите его туда, он предполагает, что вы хотели поместить его туда. IntelliJ с радостью настроит для вас пространство вкладок.


IntelliJ также можно настроить, чтобы сделать некоторые из этих действий за вас. В разделе «Настройки» -> «стиль кода» -> «java» на вкладке «Обертка и фигурные скобки» вы можете установить «вызовы цепных методов» на «всегда перенос».

Перед автоматическим форматированием

// Mulit-Line Statement
List<String> strings = Arrays.stream(ppl).filter((x) -> { return x.contains("(M)"); }).collect(Collectors.toList());

// Single-Line Statement
List<String> strings = Arrays.stream(ppl).map((x) -> x.toUpperCase()).filter((x) -> x.contains("(M)")).collect(Collectors.toList());

После автоматического форматирования

// Mulit-Line Statement
List<String> strings = Arrays.stream(ppl)
        .filter((x) -> {
            return x.contains("(M)");
        })
        .collect(Collectors.toList());

// Single-Line Statement
List<String> strings = Arrays.stream(ppl)
        .map((x) -> x.toUpperCase())
        .filter((x) -> x.contains("(M)"))
        .collect(Collectors.toList());
person Mike Rylander    schedule 19.07.2014
comment
Вопрос в том, способен ли он форматировать его таким образом автоматически? Это упрощает проблему, если автоматическое форматирование хорошо справляется с форматированием в соответствии с тем, как мне нравится стилизовать код, таким образом, любой код легче читать, даже код любого другого. - person EpicPandaForce; 20.07.2014
comment
@Zhuinden Я обновил ответ примером того, как это сделать для однострочного оператора. Как показано, это также работает в некоторой степени для многострочного оператора. Существует много вариантов настройки автоматического форматирования IntelliJ, вы, вероятно, могли бы уточнить его, чтобы он отображался точно так, как он есть в вашем вопросе. - person Mike Rylander; 20.07.2014
comment
Я настроил все в соответствии с XML, который я вставил выше, я думаю, что буду использовать IntelliJ вместо Eclipse для всего, что не является Android. Спасибо. - person EpicPandaForce; 20.07.2014
comment
@EpicPandaForce, почему бы вам не использовать его и для Android? - person herman; 25.11.2014
comment
@herman технически, с тех пор я тоже начал использовать Android Studio для Android. Мне пришлось установить несколько дополнительных гигабайтов ОЗУ, но у фирмы было кое-что, поэтому теперь с 8 ГБ Gradle не зависает весь компьютер во время компиляции: P - person EpicPandaForce; 26.11.2014

arrow_upward
63
arrow_downward

В Eclipse для однострочных операторов:

В вашем проекте или глобальных настройках перейдите к Java -> Code Style -> Formatter -> Edit -> Line Wrapping -> Function Calls -> Qualified Invocations, установите Wrap all elements, except first if not necessary и отметьте Force split, even if line shorter than maximum line width.

person Arend    schedule 28.12.2015
comment
Эй, это кажется нормальным, за исключением того, что я вынужден сделать отступ, это тоже либо с отступом по умолчанию, либо со столбцом, либо с 1. Это не имеет смысла. Мой отступ отличается в зависимости от тока. Например: окончательный список‹DataRow› days = MappedSelect.query(gameDays, DataRow.class) .param(seasonId, 20142015) .select(context); Для вышеизложенного я хочу, чтобы следующие 2 строки были с отступом точно в точке предыдущей строки. - person Saravana Kumar M; 22.12.2016
comment
Практически идеально! :-) Иногда, однако, это слишком много для меня: утверждения внутри лямбды, например. .filter(c -> c.getValue().equals(newValue)) также будет обернут непосредственно перед .equals. Было бы неплохо, если бы такая настройка применялась только к методу, связанному с лямбдой; Я вижу, что это может быть трудно реализовать, хотя... - person Dominic; 07.03.2018

arrow_upward
24
arrow_downward

Eclipse (Mars) имеет возможность форматирования лямбда-выражений.

Go to Window > Preferences > Java > Code Style > Formatter

введите описание изображения здесь

Нажмите кнопку Edit, перейдите к тегу Braces и установите Lambda Body на Next Line Indented.

введите описание изображения здесь

Другой вариант — обновить эти свойства в настройках вашего проекта. (yourWorkspace > yourProject > .settings > org.eclipse.jdt.core.prefs)

org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=next_line_shifted
person Diego D    schedule 20.10.2015
comment
это, кажется, помогает только с лямбда-выражениями в квадратных скобках. - person Nick; 11.02.2016
comment
обновление этих свойств не помогает - person Piotr Wittchen; 04.02.2019

arrow_upward
20
arrow_downward

Этот вопрос устарел, и, к сожалению, стандартная конфигурация средства форматирования Eclipse по-прежнему неудобна для написания функционального кода в удобочитаемом виде.

Я пробовал все, что упоминалось во всех других ответах, и ни один из них не подходит для большинства случаев использования.
Это может быть хорошо для некоторых, но неприятно для других.

Я нашел способ, который подходит мне больше всего.
Я делюсь им, думая, что он может помочь другим.

Обратите внимание, что у моего пути есть компромисс: принятие того, что каждый квалифицированный вызов всегда должен находиться в отдельной строке.
Возможно, это отсутствующая опция в конфигурации средства форматирования: указание порога с точки зрения вызовов для переноса строки вместо использования 1 вызов по умолчанию.

Вот мои 2 комбинированных инструмента, чтобы справиться с этим довольно правильно:

  • Настройка конфигурации средства форматирования Eclipse для большинства случаев

  • Создание шаблона кода с //@formatter:off ... //@formatter:on для крайних случаев.


Настройка конфигурации средства форматирования Eclipse
Значение, которое нужно изменить, обведено красным в захвате.

Шаг 1) Создайте собственный форматировщик стиля кода Java

Preferences и в дереве перейдите к Java -> Code Style -> Formatter.
Нажмите "Создать", чтобы создать новый Profile (инициализируйте его с помощью "Соглашений Java").

Форматировщик Eclipse

Следующие два шага необходимо выполнить в пользовательском профиле средства форматирования.

Шаг 2. Измените конфигурацию отступов для строк с переносом

Конфигурация идентификации

Модификация позволяет использовать пробелы вместо табуляции.
Это будет иметь значение на следующем шаге, когда мы настроим политику переноса строк с опцией отступа столбца.
Это действительно позволит избежать создания неприятных пробелов.

Шаг 3. Измените отступ по умолчанию для строк с переносом и политику переноса строк для квалифицированного вызова

Размер строки с переносом


Вот тестовое форматирование с кодом вопроса.

Перед форматированием :

void multiLineStatements() {
    String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" };
    List<String> strings = Arrays.stream(ppl).filter((x) ->
    {
        return x.contains("(M)");
    }).collect(Collectors.toList());
    strings.stream().forEach(System.out::println);
}

void singleLineStatements() {
    String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des(M)", "Rick (M)" };
    List<String> strings = Arrays.stream(ppl).map((x) -> x.toUpperCase())
            .filter((x) -> x.contains("(M)")).collect(Collectors.toList());
    strings.stream().forEach(System.out::println);
}

После форматирования :

void multiLineStatements() {
    String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" };
    List<String> strings = Arrays.stream(ppl)
                                 .filter((x) -> {
                                     return x.contains("(M)");
                                 })
                                 .collect(Collectors.toList());
    strings.stream()
           .forEach(System.out::println);
}

void singleLineStatements() {
    String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des(M)", "Rick (M)" };
    List<String> strings = Arrays.stream(ppl)
                                 .map((x) -> x.toUpperCase())
                                 .filter((x) -> x.contains("(M)"))
                                 .collect(Collectors.toList());
    strings.stream()
           .forEach(System.out::println);
}

Создание шаблонов кода с помощью //@formatter:off ... //@formatter:on для крайних случаев.

Писать вручную или копировать-вставлять //@formatter:on и //@formatter:off нормально, так как вы пишете их редко.
Но если вам приходится писать их несколько раз в неделю или, что еще хуже, днем, более автоматический способ приветствуется.

Шаг 1) Перейдите к шаблону редактора Java

Preferences и в дереве перейдите к Java ->Editor -> Template.

Шаг 2) Создайте шаблон, чтобы отключить форматирование для выбранного кода

Форматирование шаблона выключено

Теперь вы можете протестировать его.
Выберите строки, для которых вы хотите отключить форматирование.
Теперь дважды введите ctrl+space (первый – "Предложения Java", второй – "Предложения шаблона").
Вы должны получить что-то вроде:

шаблон предложения

Выберите шаблон fmt как на скриншоте и нажмите «Войти». Готово!

результат после применения шаблона

person davidxxx    schedule 18.12.2017

arrow_upward
16
arrow_downward

Я форматирую однострочный оператор, добавляя пустой комментарий "//" после functions.

List<Integer> list = Arrays.stream(x) //
                           .filter((n) -> n % 2 == 0) //
                           .map((n) -> n * 4) //
                           .boxed() //
                           .collect(Collectors.toList());
person Saljack    schedule 10.02.2017
comment
Легко и эффективно. - person Steve; 24.04.2018
comment
Я думаю, что это шум в вашей кодовой базе только из-за того, что вы не установили конфигурацию стиля IDE. Это может сбить с толку людей, которые впервые смотрят на вашу кодовую базу. - person Marc Bouvier; 17.09.2018

arrow_upward
9
arrow_downward

Не идеально, но вы можете отключить форматирование только для тех разделов, которые немного плотные. Например

  //@formatter:off
  int sum = Arrays.stream(x)
        .map((n) -> n * 5)
        .filter((n) -> {
            System.out.println("Filtering: " + n);
            return n % 3 != 0;
        })
        .reduce(0, Integer::sum);
  //@formatter:on

Перейдите в «Окно»> «Настройки»> «Java»> «Стиль кода»> «Форматтер». Нажмите кнопку "Редактировать...", перейдите на вкладку "Выкл./Вкл. теги" и включите теги.

person throp    schedule 03.12.2014
comment
Это казалось неуклюжим, но более простым вариантом правильного форматирования. - person smc; 17.03.2015

arrow_upward
2
arrow_downward

Вариант, который работал у меня, заключался в том, чтобы отметить параметр Never join already wrapped lines в разделе «Перенос строк» ​​модуля форматирования в настройках.

person Nosrep    schedule 02.06.2020

arrow_upward
1
arrow_downward

Если у вас еще нет пользовательского средства форматирования Eclipse:

Настройки Eclipse Java › Стиль кода › Formatter New Введите имя Нажмите «ОК» Управление разрывами строк

Отредактируйте вкладку «Перенос строк» ​​в профиле. Установите флажок «Никогда не соединять уже перенесенные строки».

Это закончится так

    String phrase = employeeList
            .stream()
            .filter(p -> p.getAge() >= 33)
            .map(p -> p.getFirstName())
            .collect(Collectors.joining(" and ", "In Germany ", " are of legal age."));

Нужно вводить каждый . начать со следующей строки

кредиты - https://www.selikoff.net/2017/07/02/eclipse-and-line-wrapping/

person Somendu    schedule 12.09.2020