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.)



EDIT: самое близкое, что я мог получить, было с IntelliJ IDEA 13 Community Edition (read: free 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+Ф, клавиши AltGr+г, клавиши AltGr+Б, клавиши AltGr+N и клавиши AltGr+м (что соответствует сочетание клавиш Ctrl+АЛТ).

<?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, похоже, не предоставляет способ поместить открывающую скобку лямбды в новую строку, в противном случае это довольно разумный способ форматирования, поэтому я отмечу это как принятое.

6 61

6 ответов:

из коробки 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());

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

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

Eclipse (Mars) имеет опцию для форматирования лямбда-выражений.

на Window > Preferences > Java > Code Style > Formatter

enter image description here

выберите Edit кнопка, перейти к Braces - тег и выберите Lambda Body до Next Line Indented

enter image description here

другой вариант-обновить эти свойства в настройках проекта. (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

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

List<Integer> list = Arrays.stream(x) //
                           .filter((n) -> n % 2 == 0) //
                           .map((n) -> n * 4) //
                           .boxed() //
                           .collect(Collectors.toList());

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

  //@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 > стиль кода > форматер". Нажмите кнопку " Изменить..."кнопка, перейдите на вкладку" Off / On Tags " и включите теги.

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

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

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

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

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

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

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


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

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

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

Eclipse formatter

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

Шаг 2) Измените конфигурацию отступа для обернутых линий

Identation configuration

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

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

Wrapped line size


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

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

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.
enter image description here

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

Template Formatter off on

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

template proposal

выберите fmt шаблон как на скриншоте и нажимаем "Enter". Готово!

result after template application