Java 8: Formatowanie lambda z nowymi liniami i wcięciami

83

To, co chciałbym osiągnąć za pomocą wcięcia lambda, jest następujące:

Instrukcja wielowierszowa:

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

Instrukcja jednowierszowa:

List<String> strings = Arrays.stream(ppl)
                         .map((x) -> x.toUpperCase())
                         .filter((x) -> x.contains("(M)"))
                         .collect(Collectors.toList());



Obecnie Eclipse automatycznie formatuje w następujący sposób:

Instrukcja wielowierszowa:

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

Instrukcja jednowierszowa:

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

Uważam, że jest to naprawdę nieporządne, ponieważ collectpołączenie jest bezpośrednio pod spodem returni nie ma między nimi żadnej przestrzeni. Wolałbym, gdybym mógł rozpocząć lambdę w nowym wierszu z wcięciem, tak aby .filter(wywołanie znajdowało się tuż nad .collect(wywołaniem. Jednak jedyną rzeczą, którą można dostosować za pomocą standardowego komponentu Java-8 Eclipse Formatter, jest nawias klamrowy na początku ciała lambda, ale nie ma ()wcześniejszego nawiasu ani wcięcia.

A w przypadku połączeń jednowierszowych po prostu używa podstawowego zawijania linii i sprawia, że ​​jest to łańcuchowy bałagan. Chyba nie muszę wyjaśniać, dlaczego później trudno to odszyfrować.

Czy jest jakiś sposób, aby w jakiś sposób bardziej dostosować formatowanie i osiągnąć pierwszy typ formatowania w Eclipse? (Lub opcjonalnie w innym IDE, takim jak IntelliJ IDEA).



EDYCJA: Najbliższe, jakie mogłem uzyskać, było z IntelliJ IDEA 13 Community Edition (czytaj: wydanie bezpłatne: P), które było następujące (zdefiniowane przez ciągłe wcięcie, które w tym przypadku wynosi 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);    

Pozwala także na „wyrównanie” połączonego wywołania metody w następujący sposób:

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

Osobiście uważam, że chociaż ma to większy sens, druga wersja odsuwa ją zdecydowanie za daleko, więc wolę pierwszą.

Konfiguracja odpowiedzialna za pierwszą konfigurację jest następująca:

<?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>

Próbowałem się upewnić, że wszystko jest rozsądne, ale mogłem coś zepsuć, więc może wymagać drobnych poprawek.

Jeśli jesteś Węgrem, tak jak ja i używasz układu węgierskiego, ta mapa klawiszy może ci się przydać, więc nie będziesz w stanie użyć AltGR + F, AltGR + G, AltGR + B , AltGR + N i AltGR + M (co odpowiada 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>

Chociaż IntelliJ nie wydaje się zapewniać sposobu na umieszczenie nawiasu otwierającego lambda w nowej linii, w przeciwnym razie jest to dość rozsądny sposób formatowania, więc oznaczę to jako zaakceptowane.

EpicPandaForce
źródło
10
Nie możesz użyć .filter(x -> x.contains("(M)"))? Dużo prostsze ... Jeśli faktycznie mówisz o miejscach, w których potrzebujesz wielu stwierdzeń, lepiej byłoby podać przykład, który tego potrzebował.
Jon Skeet,
@JonSkeet Nie mogłem teraz wymyślić naprawdę dobrego przypadku użycia dla wielu linii, ale zamiast tego dodałem oba przypadki.
EpicPandaForce
2
Szczerze mówiąc, wersja „Netbeans 8.0”, którą ktoś opublikował poniżej i usunęła, była naprawdę bliska prawdy, chociaż Netbeans jest znany z tego, że nie pozwala na prawidłowe formatowanie otwierających nawiasów klamrowych w stylu Allmana.
EpicPandaForce
3
Wydaje mi się, że obecne rozwiązanie umożliwia „nigdy nie dołączaj zawiniętych wierszy” i formatowanie ręczne, ale wydaje się to nieco żmudne w porównaniu do naciskania, Ctrl+Alt+Fjeśli automatycznie formatujesz czyjś kod.
EpicPandaForce
Z jakiegoś powodu Eclipse miał problemy z ustaleniem typu xbez otwierania nawiasu klamrowego i zamykania go, dlatego na początku użyłem oświadczenia zamiast prostej wersji.
EpicPandaForce

Odpowiedzi:

30

Po wyjęciu z pudełka IntelliJ 13 prawdopodobnie będzie dla Ciebie działać.

Jeśli napiszę to w ten sposób:

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

A następnie zastosuj automatyczny formatter (bez zmian):

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

To samo dotyczy instrukcji jednowierszowych. Z mojego doświadczenia wynika, że ​​IntelliJ jest bardziej elastyczny w stosowaniu automatycznego formatowania. IntelliJ rzadziej usunie lub doda zwroty linii, jeśli umieścisz go tam, to zakłada, że ​​zamierzałeś go tam umieścić. IntelliJ z przyjemnością dostosuje dla Ciebie miejsce na karcie.


IntelliJ można również skonfigurować tak, aby zrobił to za Ciebie. W sekcji „ustawienia” -> „styl kodu” -> „java”, w zakładce „Zawijanie i nawiasy klamrowe” można ustawić „łańcuchowe wywołania metod” na „zawijaj zawsze”.

Przed automatycznym formatowaniem

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

Po automatycznym formatowaniu

// 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());
Mike Rylander
źródło
Pytanie brzmi, czy jest w stanie sformatować go w ten sposób automatycznie? Upraszcza problem, jeśli automatyczne formatowanie dobrze sobie radzi z formatowaniem go tak, jak lubię stylizować kod, dzięki czemu każdy kod jest łatwiejszy do odczytania, nawet kod innej osoby.
EpicPandaForce,
@Zhuinden Zaktualizowałem odpowiedź, podając przykład, jak to zrobić dla instrukcji jednowierszowej. Jak pokazano, działa to również do pewnego stopnia w przypadku instrukcji wielowierszowej. Istnieje wiele opcji konfiguracji automatycznego formatowania IntelliJa, prawdopodobnie możesz go udoskonalić, aby wyglądał dokładnie tak, jak masz pytanie.
Mike Rylander
Skonfigurowałem wszystko zgodnie z XML, który wkleiłem powyżej, myślę, że będę używać IntelliJ zamiast Eclipse do wszystkiego, co nie jest Androidem. Dzięki.
EpicPandaForce,
1
@EpicPandaForce, dlaczego nie miałbyś go używać również na Androida?
herman
@herman od tego czasu zacząłem używać Android Studio na Androida. Musiałem zainstalować kilka dodatkowych gigabajtów pamięci RAM, ale firma miała trochę dookoła, więc teraz z 8 GB Gradle nie zamraża całego komputera podczas kompilacji: P
EpicPandaForce
59

W Eclipse dla instrukcji jednowierszowych:

W swoim projekcie lub preferencjach globalnych przejdź do Java -> Code Style -> Formatter -> Edit -> Line Wrapping -> Function Calls -> Qualified Invocations, ustaw Wrap all elements, except first if not necessaryi zaznacz Force split, even if line shorter than maximum line width.

Arend
źródło
Hej, wydaje się to w porządku, z wyjątkiem tego, jestem zmuszony do wcięcia, też z domyślnym wcięciem, kolumną lub 1. To nie ma sensu. Moje wcięcie różni się w zależności od prądu. Np .: final List <DataRow> days = MappedSelect.query ("gameDays", DataRow.class) .param ("seasonId", 20142015) .select (context); W związku z powyższym chcę, aby następne 2 wiersze były wcięte dokładnie na kropce poprzedniego wiersza.
Saravana Kumar M
1
Prawie idealnie! :-) Czasami jednak zawija się za dużo jak na moje preferencje: instrukcje wewnątrz lambdy, np. .filter(c -> c.getValue().equals(newValue))Będą również zawijane tuż przed .equals. Byłoby fajnie, gdyby takie ustawienie dotyczyło tylko metody związanej z lambdą; Widzę, że może to być trudne do zrealizowania ...
Dominic
24

Eclipse (Mars) ma opcję programu formatującego wyrażenia lambda.

Iść do Window > Preferences > Java > Code Style > Formatter

wprowadź opis obrazu tutaj

Kliknij Editprzycisk, przejdź do Bracesznacznika i ustaw Lambda BodynaNext Line Indented

wprowadź opis obrazu tutaj

Inną opcją jest zaktualizowanie tych właściwości w ustawieniach projektu. ( 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
Diego D.
źródło
2
wydaje się, że pomaga to tylko w przypadku lambd w nawiasach.
Nick
aktualizacja tych nieruchomości nie pomaga
Piotr Wittchen
19

To pytanie jest teraz stare i niestety domyślna konfiguracja programu formatującego Eclipse nadal nie jest przyjazna dla użytkownika do pisania kodu funkcjonalnego w czytelny sposób.

Wypróbowałem wszystkie rzeczy wymienione we wszystkich innych odpowiedziach i nikt nie pasuje do większości przypadków użycia.
Dla niektórych może to być dobre, ale dla innych nieprzyjemne.

Przez większość czasu znalazłem sposób, który mi odpowiada.
Dzielę się tym, myśląc, że może to pomóc innym.

Zauważ, że mój sposób wymaga kompromisu: zaakceptowanie, że każde kwalifikowane wywołanie zawsze znajduje się w odrębnej linii.
Być może jest to brakująca opcja w konfiguracji programu formatującego: wskazanie progu pod względem wywołań do zawijania wiersza zamiast używania 1wywołania domyślnie.

Oto moje 2 połączone narzędzia do obsługi raczej poprawnie:

  • Dostosowywanie konfiguracji programu formatującego Eclipse w większości przypadków

  • Tworzenie szablonu kodu //@formatter:off ... //@formatter:ondla przypadków narożnych.


Dostosowywanie konfiguracji programu formatującego Eclipse
Wartości do zmiany są zaznaczone na czerwono w przechwyceniu.

Krok 1) Utwórz własny program do formatowania stylu kodu w języku Java

Preferencesmenu iw drzewie przejdź do Java -> Code Style -> Formatter.
Kliknij „Nowy”, aby utworzyć nowy Profile(zainicjuj go z „Konwencjami Java”).

Program formatujący Eclipse

Dwa następne kroki należy wykonać w swoim niestandardowym profilu formatyzatora.

Krok 2) Zmień konfigurację wcięć dla zawijanych wierszy

Konfiguracja identyfikacji

Modyfikacja pozwala na użycie białych znaków zamiast tabel.
Będzie to miało znaczenie w następnym kroku, ponieważ skonfigurujemy zasady zawijania wierszy z opcją wcięcia kolumny.
Uniknie, rzeczywiście, tworzy nieprzyjemne przestrzenie.

Krok 3) Zmień domyślne wcięcie dla zawijanych wierszy i zasady zawijania wierszy dla kwalifikowanego wywołania

Rozmiar zawiniętej linii


Oto formatowanie testu z kodem pytania.

Przed formatowaniem:

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

Po sformatowaniu:

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

Tworzenie szablonów kodu //@formatter:off ... //@formatter:ondla przypadków narożnych.

Pisanie ręczne lub kopiowaniem wklej //@formatter:oni //@formatter:offjest w porządku jak piszesz to rzadko.
Ale jeśli musisz pisać to kilka razy w tygodniu lub gorzej w ciągu dnia, mile widziany jest bardziej automatyczny sposób.

Krok 1) Przejdź do szablonu Java Editor

Preferencesmenu iw drzewie przejdź do Java ->Editor -> Template.
wprowadź opis obrazu tutaj

Krok 2) Utwórz szablon, aby wyłączyć formatowanie dla wybranego kodu

Szablon Formatter wyłączony włączony

Możesz to teraz przetestować.
Wybierz linie, dla których chcesz wyłączyć formatowanie.
Teraz wprowadź ctrl+spacedwukrotnie (pierwsza to „Propozycje Java”, a druga to „Propozycje szablonów”).
Powinieneś dostać coś takiego:

propozycja szablonu

Wybierz fmtszablon jak na zrzucie ekranu i kliknij „Enter”. Gotowe!

wynik po zastosowaniu szablonu

davidxxx
źródło
16

Formatuję instrukcję jednowierszową, dodając pusty komentarz „//” po funkcjach.

List<Integer> list = Arrays.stream(x) //
                           .filter((n) -> n % 2 == 0) //
                           .map((n) -> n * 4) //
                           .boxed() //
                           .collect(Collectors.toList());
Saljack
źródło
3
Łatwe i skuteczne.
Steve
12
Myślę, że to szum w twoim kodzie tylko dlatego, że nie ustawiłeś konfiguracji stylu IDE. Może to być mylące dla osób, które po raz pierwszy przeglądają Twój kod źródłowy.
Marc Bouvier
9

Nie jest to idealne rozwiązanie, ale możesz wyłączyć formatyzator tylko dla tych sekcji, które są trochę gęste. Na przykład

  //@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

Wybierz „Okno> Preferencje> Java> Styl kodu> Formatyzator”. Kliknij przycisk „Edytuj ...”, przejdź do zakładki „Wyłącz / Włącz tagi” i włącz tagi.

throp
źródło
3
Wydawało się to nieprzyjemną, ale prostszą opcją, aby poprawić moje formatowanie
smc
2

Opcją, która działała dla mnie, było zaznaczenie Never join already wrapped linesopcji w sekcji Zawijanie linii w programie Formatter w Preferencjach.

Nosrep
źródło
1

Jeśli nie masz jeszcze niestandardowego programu formatującego Eclipse:

Preferencje Eclipse Java> Styl kodu> Formatter Nowy Wprowadź nazwę Kliknij OK Kontroluj podziały wierszy

Edytuj profil Zakładka Zawijanie linii Zaznacz „Nigdy nie dołączaj już zawiniętych linii”

To się tak skończy

    String phrase = employeeList
            .stream()
            .filter(p -> p.getAge() >= 33)
            .map(p -> p.getFirstName())
            .collect(Collectors.joining(" and ", "In Germany ", " are of legal age."));

Trzeba wpisać co. zacznij w następnej linii

kredyty - https://www.selikoff.net/2017/07/02/eclipse-and-line-wrapping/

Somendu
źródło
-1

Najnowsza wersja Eclipse ma wbudowane formatowanie java 8, goto

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

and Select Eclipse 2.1[built-in] profile
Yuvaraj Chitela
źródło