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

1行のステートメント:

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

1行のステートメント:

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で最初のフォーマットタイプを実現する方法はありますか?(または、オプションで、IntelliJ IDEAなどの別のIDEで。)



編集:私が得ることができた最も近いものは、次のような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);
}

個人的には、それは理にかなっていますが、2番目のバージョンではあまりにも遠くに押し出されているので、最初のバージョンの方が好きです。

最初のセットアップを担当するセットアップは次のとおりです。

<?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)"))か?はるかに簡単です...実際に複数のステートメントが必要な場所について話している場合は、それを必要とする例を示す方がよいでしょう。
Jon Skeet 2014

@JonSkeet現時点では、複数行の本当に良いユースケースを考えることはできませんでしたが、代わりに両方のケースを追加しました。
EpicPandaForce 2014

2
正直なところ、誰かが以下に投稿して削除した「Netbeans 8.0」バージョンは、実際に真実に近いものでしたが、Netbeansは、ステートメントの開始中括弧をAllmanスタイルに適切にフォーマットできないことで有名です。
EpicPandaForce 2014

3
現在の解決策は、「折り返し行を結合しない」と手動でのフォーマットを有効にすることだとCtrl+Alt+F思いますが、他の人のコードを自動フォーマットする場合に押すのに比べると、やや面倒なようです。
EpicPandaForce 2014

そして、何らかの理由で、Eclipseはx中括弧を開いて閉じずにタイプを理解するのに問題がありました。そのため、最初は単純なバージョンではなくステートメントを使用しました。
EpicPandaForce 2014

回答:


30

箱から出してすぐにIntelliJ13がおそらくあなたのために働くでしょう。

私がこのように書くと:

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

同じことが1行のステートメントにも当てはまります。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 2014

@ Zhuinden1行のステートメントでこれを行う方法の例で回答を更新しました。示されているように、複数行のステートメントでもある程度機能します。IntelliJのオートフォーマッターを構成するための多くのオプションがあります。おそらく、質問にあるとおりに表示されるように改良することができます。
マイク・ライランダー2014

上に貼り付けたXMLに従って設定しました。Android以外の場合は、EclipseではなくIntelliJを使用すると思います。ありがとう。
EpicPandaForce 2014

1
@EpicPandaForceなぜAndroidにも使用しないのですか?
ハーマン2014年

@herman技術的には、それ以来、Android Studio forAndroidの使用も開始しています。RAMを数ギガ追加インストールする必要がありましたが、会社にはいくつかの敷設がありました。現在8 GBであるため、Gradleはコンパイル中にコンピューター全体をフリーズしません:P
EpicPandaForce 2014年

59

Eclipseでは、1行のステートメントの場合:

プロジェクトまたはグローバル設定で、に移動し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行を前の行ドットに正確にインデントしたいと思います。
サラバナクマールM 2016

1
ほぼ完璧!:-)ただし、私の好みではラップしすぎることがあります。ラムダ内のステートメント。たとえば.filter(c -> c.getValue().equals(newValue))、.equalsの直前にもラップされます。このような設定がラムダ関連のメソッドにのみ適用されるとしたら、それは素晴らしいことです。しかし、これを実装するのは難しいかもしれません...
ドミニク

24

Eclipse(Mars)には、ラムダ式フォーマッターのオプションがあります。

に移動 Window > Preferences > Java > Code Style > Formatter

ここに画像の説明を入力してください

ボタンをクリックEditし、Bracesタグに移動して、に設定Lambda BodyしますNext Line Indented

ここに画像の説明を入力してください

もう1つのオプションは、これらのプロパティをプロジェクト設定に更新することです。(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
これは、括弧で囲まれたラムダにのみ役立つようです。
ニック

これらのプロパティを更新しても役に立ちません
PiotrWittchen19年

19

この質問は古くなりましたが、残念ながら、Eclipseフォーマッターのデフォルト構成は、関数型コードを読みやすい方法で作成するのにまだユーザーフレンドリーではありません。

私は他のすべての回答で言及されているすべてのことを試しましたが、ほとんどのユースケースに適している人はいませんでした。
一部の人にとっては問題ないかもしれませんが、他の人にとっては不快かもしれません。

私はほとんどの場合自分に合った方法を見つけました。
私はそれが他の人を助けることができると考えてそれを共有します。

私の方法にはトレードオフがあることに注意してください。修飾された各呼び出しが常に別個の行にあることを受け入れることです。
フォーマッタ構成に欠落しているオプションである可能性があり1ます。デフォルトで呼び出しを使用する代わりに、呼び出しに関してしきい値を示し、行を折り返します。

これは、それをかなり正しく処理するための2つの組み合わせツールです。

  • ほとんどの場合のEclipseフォーマッター構成のカスタマイズ

  • //@formatter:off ... //@formatter:onコーナーケース用のコードテンプレートを作成します 。


Eclipseフォーマッター構成
カスタマイズ変更する値は、キャプチャー内で赤で囲まれています。

ステップ1)独自のJavaコードスタイルフォーマッタを作成する

Preferencesメニューとツリーで、に移動し Java -> Code Style -> Formatterます。
「新規」をクリックして、新しいものを作成しますProfile(「Java規則」で初期化します)。

Eclipseフォーマッター

次の2つの手順は、カスタムフォーマッタプロファイルで実行する必要があります。

手順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)JavaEditorテンプレートに移動します

Preferencesメニューとツリーで、に移動し Java ->Editor -> Templateます。
ここに画像の説明を入力してください

ステップ2)選択したコードのフォーマットを無効にするテンプレートを作成します

テンプレートフォーマッタオフオン

これでテストできます。
書式設定を無効にする行を選択します。
ここでctrl+space2回入力します(1つ目は「Javaプロポーザル」で、2つ目は「テンプレートプロポーザル」です)。
あなたは次のようなものを手に入れるべきです:

テンプレート提案

fmtスクリーンショットのようにテンプレートを選択し、「Enter」をクリックします。完了!

テンプレート適用後の結果


16

関数の後に空のコメント「//」を追加して、1行のステートメントをフォーマットします。

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

3
簡単で効果的。
スティーブ

12
IDEスタイルの構成を設定しないというだけのコードベースのノイズだと思います。コードベースを初めて見る人にとっては混乱を招く可能性があります。
Marc Bouvier 2018

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には、フォーマッターjava8フォーマットが組み込まれています。

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

and Select Eclipse 2.1[built-in] profile
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.