Подтвердить что ты не робот

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

4b9b3361

Ответ 1

Возможно, вам будет полезен 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" на вкладке "Обертка и привязки" вы можете установить "цепные вызовы методов" на "wrap always".

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

// 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());

Ответ 2

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

Ответ 3

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

Перейдите к 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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Обратите внимание, что мой способ имеет компромисс: принятие того, что каждый квалифицированный вызов всегда находится на отдельной строке.
Возможно, это недостающий параметр в конфигурации форматирования: указание порога в терминах invocations для обертывания строки вместо использования 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) Создайте шаблон, чтобы отключить форматирование для выбранного кода

Formatter Formatter off on

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

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

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

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