Java8: 使用换行和缩进格式化 lambda

我希望通过 Lambda 压痕实现以下目标:

多行语句:

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下面的,而且中间没有任何空间。我希望如果我可以开始在一个新的行缩进的 lambda,这样,.filter(调用将正好高于 .collect(调用。但是,可以用标准 Java-8 Eclipse Formatter 定制的惟一内容是 lambda 主体开始处的大括号,但是在此之前不需要使用 ()括号,也不需要缩进。

在单行调用的情况下,它只使用基本的换行,使其成为一个链式混乱。我想我不需要解释为什么事后很难解密。

有没有什么方法可以更多地自定义格式并在 Eclipse 中实现第一种格式类型?(或者,可选地,在另一个 IDE 中,如 IntelliJIDEA。)



编辑: 我能得到的最接近的是 IntelliJ IDEA 13社区版(阅读: 免费版: 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 似乎没有提供一种将 lambda 的开头大括号放在新行中的方法,但它是一种相当合理的格式化方法,因此我将把它标记为接受。

60215 次浏览

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

Not ideal, but you could turn the formatter off for just those sections that are a little dense. For example

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

Go to "Window > Preferences > Java > Code Style > Formatter". Click the "Edit..." button, go to the "Off/On Tags" tab and enable the tags.

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

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.

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

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

The option that worked for me was ticking the Never join already wrapped lines option in the Line Wrapping section of the Formatter in Preferences.

If you don’t already have a custom Eclipse formatter:

Eclipse preferences Java > Code Style > Formatter New Enter a name Click ok Control line breaks

Edit the profile Line wrapping tab Check “Never join already wrapped lines”

It will end up like this

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

Need to type every . start in the next line

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