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)


Из коробки 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

В 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

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

Этот вопрос устарел, и, к сожалению, стандартная конфигурация средства форматирования 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

Я форматирую однострочный оператор, добавляя пустой комментарий "//" после 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

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

  //@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

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

person Nosrep    schedule 02.06.2020

Если у вас еще нет пользовательского средства форматирования 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