Java 8: Formatting lambda with newlines and indentation

55,618

Solution 1

Out of the box IntelliJ 13 will probably work for you.

If I write it this way:

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

And then apply the auto formatter (no changes):

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

The same is true for your single line statement. It has been my experience that IntelliJ is more flexible in how its auto formatting is applied. IntelliJ is less likely to remove or add line returns, if you put it there then it assumes you meant to put it there. IntelliJ will happily adjust your tab-space for you.


IntelliJ can also be configured to do some of this for you. Under "settings" -> "code style" -> "java", in the "Wrapping and Braces" tab you can set "chain method calls" to "wrap always".

Before Auto-Formatting

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

After Auto-Formatting

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

Solution 2

In Eclipse, for the single-line statements:

In your project or global preferences, go to Java -> Code Style -> Formatter -> Edit -> Line Wrapping -> Function Calls -> Qualified Invocations, set Wrap all elements, except first if not necessary and tick Force split, even if line shorter than maximum line width.

Solution 3

Eclipse (Mars) has an option for lambda expressions formatter.

Go to Window > Preferences > Java > Code Style > Formatter

enter image description here

Click the Edit button, go to the Braces Tag and set the Lambda Body to Next Line Indented

enter image description here

Another option is update these properties into your project settings. (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

Solution 4

This question is now old and unfortunately, the default configuration of the Eclipse formatter remains still not user-friendly to write functional code in a readable way.

I tried all things mentioned in all other answers and no one suits for most of use cases.
It may be fine for some but unpleasant for others.

I found a way that suits for me the most of the time.
I share it by thinking that it could help others.

Note that my way has a trade-off : accepting that each qualified invocation be always on a distinct line.
It is maybe the missing option in the formatter configuration : indicating the threshold in terms of invocations to wrap the line instead of using 1 invocation by default.

Here are my 2 combined tools to handle it rather correctly :

  • Customizing Eclipse formatter configuration for most of cases

  • Creating a code template with //@formatter:off ... //@formatter:on for corner cases.


Customizing Eclipse formatter configuration
The value to change are surrounded in red in the capture.

Step 1) Create your own Java Code Style Formatter

Preferences menu and in the tree, go to Java -> Code Style -> Formatter.
Click on "New" to create a new Profile (initialize it with "Java conventions").

Eclipse formatter

The two next steps have to be performed in your custom formatter profile.

Step 2) Change the indentation configuration for wrapped lines

Identation configuration

The modification allows to use whitespaces instead of tabulations.
It will matter in the next step as we configure the line wrapping policy with the column indentation option.
It will avoid indeed creates unpleasant spaces.

Step 3) Change the default indentation for wrapped lines and the line wrapping policy for qualified invocation

Wrapped line size


Here is a test formatting with code of the question.

Before formatting :

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

After formatting :

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

Creating code templates with //@formatter:off ... //@formatter:on for corner cases.

Writing manually or copy-paste //@formatter:on and //@formatter:off is fine as you write it rarely.
But if you have to write it several times by week or even worse by day, a more automatic way is welcome.

Step 1) Go to Java Editor template

Preferences menu and in the tree, go to Java ->Editor -> Template.
enter image description here

Step 2) Create template to disable formatting for selected code

Template Formatter off on

You can now test it.
Select lines which you want to disable the formatting.
Now enter ctrl+space twice (first one is "Java proposals" and the second one is "template proposals").
You should get something like :

template proposal

Select the fmt template as in the screenshot and click "Enter". Done!

result after template application

Solution 5

I format single-line statement by adding empty comment "//" after functions.

List<Integer> list = Arrays.stream(x) //
                           .filter((n) -> n % 2 == 0) //
                           .map((n) -> n * 4) //
                           .boxed() //
                           .collect(Collectors.toList());
Share:
55,618
EpicPandaForce
Author by

EpicPandaForce

I'm an Android developer, and I answer questions for points. I also do that on /r/android_devs and /r/androiddev. Go check it out if you've never been there. I also have some articles hosted on Medium.

Updated on September 13, 2020

Comments

  • EpicPandaForce
    EpicPandaForce over 3 years

    What I would like to achieve with lambda indentation is the following:

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

    Single-line statement:

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



    Currently, Eclipse is auto-formatting to the following:

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

    Single-line statement:

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

    And I find this really messy, because of how the collect call is directly underneath the return and there's no space inbetween at all. I would prefer it if I could start the lambda in a new line indented, and so that the .filter( call would be right above the .collect( call. However, the only thing that can be customized with standard Java-8 Eclipse Formatter is the brace at the start of the lambda body, but nothing for the () brackets beforehand, nor the indentation.

    And in the case of single-line calls, it just uses the basic line-wrap and makes it be a chained mess. I don't think I need to explain why this is hard to decrypt afterwards.

    Is there any way to somehow customize the formatting more and achieve the first formatting type in Eclipse? (Or, optionally, in another IDE like IntelliJ IDEA.)



    EDIT: The closest I could get was with IntelliJ IDEA 13 Community Edition (read: free edition :P) which was the following (defined by continuous indentation which in this case is 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);    
    

    It also allows to "align" the chained method invocation like this:

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

    I personally find that while it makes more sense, the second version pushes it far too away, so I prefer the first one.

    The setup responsible for the first setup is the following:

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

    I tried to make sure everything is reasonable, but I might have messed something up, so it might need minor adjustments.

    If you're Hungarian like me and you're using a Hungarian layout, then this keymap might be of use to you, so that you don't end up not being able to use AltGR+F, AltGR+G, AltGR+B, AltGR+N and AltGR+M (which correspond to 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>
    

    While IntelliJ doesn't seem to provide a way to put the opening brace of the lambda in a new line, otherwise it's a fairly reasonable way of formatting, so I'll mark this as accepted.

  • EpicPandaForce
    EpicPandaForce almost 10 years
    The question is, is it capable of formatting it this way automatically? It simplifies the problem if auto-formatting does a good job at formatting it to the way I like to style the code, that way any code is easier to read even anyone else's code.
  • Mike Rylander
    Mike Rylander almost 10 years
    @Zhuinden I have updated the answer with an example on how to do this for a single line statement. As shown it also works to some degree for the multi-line statement. There are many many options for configuring IntelliJ's auto-formatter, you could probably refine it to appear exactly as you have it in you question.
  • EpicPandaForce
    EpicPandaForce almost 10 years
    I set things up according to the XML I pasted in above, I guess I'll be using IntelliJ instead of Eclipse for anything that isn't Android. Thanks.
  • herman
    herman over 9 years
    @EpicPandaForce why would you not use it for Android as well?
  • EpicPandaForce
    EpicPandaForce over 9 years
    @herman technically I've started using Android Studio for Android since then as well. I had to install a few extra gigs of RAM, but the firm had some laying around so now with 8 GB, Gradle doesn't freeze the whole computer while it's compiling :P
  • smc
    smc about 9 years
    This seemed a ungeeky but simpler option to get my formatting correct
  • Nick
    Nick about 8 years
    this only seems to help with bracketed lambdas.
  • Saravana Kumar M
    Saravana Kumar M over 7 years
    Hey, this seems ok except, i'm forced to indent, that too either with default indendation or column or 1. That doesn't make sense. My indentation differs based on the current. Eg: final List<DataRow> days = MappedSelect.query("gameDays", DataRow.class) .param("seasonId", 20142015) .select(context); For the above i want the next 2 lines to be indented exactly on the previous line dot.
  • Dominic
    Dominic about 6 years
    Almost perfect! :-) Sometimes however it wraps too much for my preference: Statements within the lambda, e.g. .filter(c -> c.getValue().equals(newValue)) will be also wrapped just before the .equals. Whould be nice if such a setting would only apply to lambda-related method; I see that this might be difficult to implement though...
  • Steve
    Steve about 6 years
    Easy and effective.
  • Marc Bouvier
    Marc Bouvier over 5 years
    I think it is noise in your codebase just for not setting an IDE style configuration. It can be confusing for people looking at your codebase for the first time.
  • Piotr Wittchen
    Piotr Wittchen about 5 years
    updating these properties does not help
  • Saljack
    Saljack over 2 years
    It is not just about not settings an IDE. Sometimes you want to make something more visible and put it on a newline. And in many cases you do not have a chance to change formatting style because a company/project policy. So this is only an option how to do it without formatter:off