Java 8: форматирование лямбда-выражения с помощью символов новой строки и отступов


83

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

Многострочный оператор:

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


10
Вы не можете использовать .filter(x -> x.contains("(M)"))? Гораздо проще ... Если вы действительно говорите о местах, где вам нужно несколько операторов, было бы лучше привести пример, который в этом нуждался.
Джон Скит

@JonSkeet Я не мог придумать действительно хороший вариант использования для многострочного прямо сейчас, но вместо этого я добавил оба случая.
EpicPandaForce 09

2
Честно говоря, версия Netbeans 8.0, которую кто-то разместил ниже и удалил, была действительно близка к истине, хотя Netbeans печально известен тем, что не позволяет должным образом форматировать открывающие скобки операторов в стиле Allman.
EpicPandaForce 09

3
Я предполагаю, что текущее решение позволяет «никогда не объединять обернутые строки» и форматировать вручную, но это кажется несколько утомительным по сравнению с нажатием, Ctrl+Alt+Fесли вы автоматически форматируете чужой код.
EpicPandaForce 09

И по какой-то причине у Eclipse возникли проблемы с определением типа xбез открытия фигурной скобки и ее закрытия, поэтому сначала я использовал оператор, а не простую версию.
EpicPandaForce 09

Ответы:


30

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

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

@Zhuinden Я обновил ответ примером того, как это сделать для однострочного оператора. Как показано, это также в некоторой степени работает для многострочного оператора. Существует множество вариантов настройки автоформатора IntelliJ, вы, вероятно, могли бы улучшить его, чтобы он выглядел именно так, как в вашем вопросе.
Mike Rylander

Я настроил все в соответствии с XML, который я вставил выше, думаю, я буду использовать IntelliJ вместо Eclipse для всего, что не является Android. Благодарю.
EpicPandaForce

1
@EpicPandaForce, почему бы вам не использовать его и для Android?
Herman

@herman технически я начал использовать Android Studio для Android с тех пор. Мне пришлось установить несколько дополнительных гигабайт оперативной памяти, но у фирмы было кое-что, так что теперь с 8 ГБ Gradle не замораживает весь компьютер во время компиляции: P
EpicPandaForce

59

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


Эй, вроде нормально, за исключением того, что я вынужден сделать отступ, это тоже либо с отступом по умолчанию, либо с столбцом, либо с 1. Это не имеет смысла. Мой отступ отличается в зависимости от текущего. Например: final List <DataRow> days = MappedSelect.query ("gameDays", DataRow.class) .param ("seasonId", 20142015) .select (context); Для приведенного выше я хочу, чтобы следующие 2 строки были с отступом точно на предыдущей точке.
Саравана Кумар М.

1
Практически идеально! :-) Иногда, однако, это слишком много для моего предпочтения: .filter(c -> c.getValue().equals(newValue))операторы в лямбде , например , также будут заключены непосредственно перед .equals. Было бы хорошо, если бы такой параметр применялся только к лямбда-методу; Я вижу, что это может быть сложно реализовать ...
Доминик

24

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

2
это помогает только с лямбдами в квадратных скобках.
Ник

обновление этих свойств не помогает
Петр

19

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

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

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

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

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

  • Настройка конфигурации форматтера 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шаблон как на скриншоте и нажмите «Enter». Готово!

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


16

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

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

3
Легко и эффективно.
Стив

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

9

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

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


3
Это показалось неудобным, но более простым вариантом для правильного форматирования
smc

2

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


1

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

Настройки Eclipse Java> Стиль кода> Форматирование Создать Введите имя Нажмите ОК Разрывы строк управления

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

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

    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/


-1

Последняя версия Eclipse имеет встроенный форматировщик java 8, goto

Preferences -> Java -> Code Style -> Formatter -> Active profile

and Select Eclipse 2.1[built-in] profile
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.