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



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, здається, не пропонує способу поставити початкову фігурну дужку лямбда в новий рядок, інакше це досить розумний спосіб форматування, тому я позначу це як прийняте.


10
Ви не можете користуватися .filter(x -> x.contains("(M)"))? Набагато простіше ... Якщо ви насправді говорите про місця, де вам потрібно кілька тверджень, було б краще навести приклад, який це потребував.
Джон Скіт,

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

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

3
Я думаю, поточне рішення дозволяє "ніколи не приєднувати загорнуті рядки" та форматування вручну, але це здається дещо нудним порівняно з натисканням, Ctrl+Alt+Fякщо ви автоматично форматуєте чужий код.
EpicPandaForce

І чомусь у Eclipse виникли проблеми з з’ясуванням типу, xне розкриваючи фігурну дужку і не закриваючи її, отже, чому я спочатку використовував твердження, а не просту версію.
EpicPandaForce

Відповіді:


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 технічно я з того часу почав використовувати 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. Це не має сенсу. Мій відступ відрізняється залежно від поточного. Наприклад: остаточний 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
здається, це допомагає лише з лямбдами, що містять дужки.
Нік

оновлення цих властивостей не допомагає
Piotr Wittchen

19

Це питання вже застаріле, і, на жаль, конфігурація за замовчуванням форматування 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 Editor

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. Це може заплутати людей, які вперше переглядають вашу базу кодів.
Марк Був'є

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> Стиль коду> Форматер Нове Введіть ім'я Клацніть ok Контроль розривів рядків

Редагування профілю Вкладка "Обтікання рядків" Позначте пункт "Ніколи не приєднуватись до вже загорнутих рядків"

Це закінчиться так

    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.