it-swarm.com.ru

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

71
EpicPandaForce

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

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

52
Arend

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
19
Diego D

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

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

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

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

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

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

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

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

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


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

Шаг 1) Создайте свой собственный Java Code Style Formatter

В меню 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

12
davidxxx

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

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

7
throp