Java 8: Formatage lambda avec retours à la ligne et indentation


Ce que je voudrais réaliser avec lambda indentation est le suivant:

Instruction multiligne:

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

Instruction à une ligne:

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



Actuellement, Eclipse est le formatage automatique suivant:

Instruction multiligne:

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

Instruction à une ligne:

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

Et je trouve cela vraiment désordonné, à cause de la façon dont l'appel collect est directement sous le return et il n'y a pas d'espace entre les deux. Je préfèrerais si Je pourrais démarrer le lambda dans une nouvelle ligne en retrait, et de sorte que l'appel .filter( soit juste au-dessus de l'appel .collect(. Cependant, la seule chose qui peut être personnalisée avec le formateur Java-8 Eclipse standard est l'accolade au début du corps lambda, mais rien pour les crochets () au préalable, ni l'indentation.

Et dans le cas des appels à une seule ligne, il utilise simplement le line-wrap de base et en fait un désordre enchaîné. Je ne pense pas avoir besoin d'expliquer pourquoi c'est difficile à décrypter ensuite.

Existe-t-il un moyen de personnaliser davantage le formatage et d'obtenir le premier type de formatage dans Eclipse? (Ou, éventuellement, dans un autreE comme IntelliJ IDEA.)



EDIT: Le plus proche que je pouvais obtenir était avec IntelliJ IDEA 13 Community Edition (lire: free edition :P) qui était le suivant (défini par indentation continue qui dans ce cas est 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);    

Il permet également d '"aligner" l'invocation de la méthode chaînée comme ceci:

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

Personnellement, je trouve que bien que cela ait plus de sens, la deuxième version la repousse beaucoup trop loin, donc je préfère la première.

La configuration responsable de la première configuration est la suivante:

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

J'ai essayé de m'assurer que tout était raisonnable, mais j'ai peut-être gâché quelque chose, donc cela pourrait nécessiter des ajustements mineurs.

Si vous êtes hongrois comme moi et que vous utilisez une disposition hongroise, alors cette carte de touches pourrait vous être utile, de sorte que vous ne finissez par ne pas pouvoir utiliser AltGR + F, AltGR+G, AltGR+B, AltGR+N et AltGR+M (qui correspondent à 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>

Bien qu'IntelliJ ne semble pas fournir un moyen de mettre l'accolade d'ouverture du lambda dans une nouvelle ligne, sinon c'est un moyen de formatage assez raisonnable, donc je vais marquer cela comme accepté.

Author: EpicPandaForce, 2014-07-09

6 answers

IntelliJ 13 prêt à l'emploi fonctionnera probablement pour vous.

Si je l'écris de cette façon:

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

Puis appliquer le formateur automatique (pas de modifications):

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

La même chose est vraie pour votre instruction à une seule ligne. Mon expérience a montré qu'IntelliJ est plus flexible dans la façon dont son formatage automatique est appliqué. IntelliJ est moins susceptible de supprimer ou d'ajouter des retours de ligne, si vous le mettez là, il suppose que vous vouliez le mettre là. IntelliJ se fera un plaisir d'ajuster votre espace tab pour vous.


IntelliJ peut également être configuré pour faire une partie de cela pour vous. Sous "paramètres" - > "style de code" - > "java", dans l'onglet" Encapsulation et accolades", vous pouvez définir" appels de méthode de chaîne "sur"encapsuler toujours".

Avant le formatage automatique

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

Après le formatage automatique

// 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());
 20
Author: Mike Rylander, 2014-07-19 21:31:54

Dans Eclipse, pour les instructions à une ligne:

Dans votre projet ou vos préférences globales, allez dans Java -> Code Style -> Formatter -> Edit -> Line Wrapping -> Function Calls -> Qualified Invocations, définissez Wrap all elements, except first if not necessary et cochez Force split, even if line shorter than maximum line width.

 39
Author: Arend, 2015-12-28 10:58:16

Eclipse (Mars) a une option pour le formateur d'expressions lambda.

Aller à Window > Preferences > Java > Code Style > Formatter

entrez la description de l'image ici

Cliquez sur le bouton Edit, accédez à la balise Braces et définissez le Lambda Body sur Next Line Indented

entrez la description de l'image ici

Une autre option consiste à mettre à jour ces propriétés dans les paramètres de votre projet. (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
 18
Author: Diego D, 2015-10-20 14:45:47

Je formate une instruction à une ligne en ajoutant un commentaire vide "//" après les fonctions.

List<Integer> list = Arrays.stream(x) //
                           .filter((n) -> n % 2 == 0) //
                           .map((n) -> n * 4) //
                           .boxed() //
                           .collect(Collectors.toList());
 12
Author: Saljack, 2017-02-10 07:19:29

Pas idéal, mais vous pouvez désactiver le formateur uniquement pour les sections un peu denses. Par exemple

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

Allez dans "Fenêtre > Préférences > Java > Style de code > Formateur". Cliquez sur le "Modifier...", allez dans l'onglet" Off / On Tags " et activez les tags.

 7
Author: throp, 2014-12-03 18:51:42

Cette question est maintenant ancienne et malheureusement, la configuration par défaut du formateur Eclipse n'est toujours pas conviviale pour écrire du code fonctionnel de manière lisible.

J'ai essayé toutes les choses mentionnées dans toutes les autres réponses et personne ne convient à la plupart des cas d'utilisation.
Cela peut être bien pour certains mais désagréable pour d'autres.

J'ai trouvé un moyen qui me convient le plus souvent.
Je le partage en pensant que cela pourrait aider les autres.

Note cette façon a un compromis : accepter que chaque invocation qualifiée soit toujours sur une ligne distincte.
C'est peut-être l'option manquante dans la configuration du formateur : indiquer le seuil en termes d'invocations pour envelopper la ligne au lieu d'utiliser l'invocation 1 par défaut.

Voici mes 2 outils combinés pour le gérer plutôt correctement:

  • Personnalisation de la configuration du formateur Eclipse pour la plupart des cas

  • Création d'un code modèle avec //@formatter:off ... //@formatter:on pour les cas d'angle.


Personnalisation de la configuration du formateur Eclipse
Les valeurs à modifier sont entourées en rouge dans la capture.

Étape 1) Créez votre propre formateur de style de code Java

Preferences menu et dans l'arbre, allez à la Java -> Code Style -> Formatter.
Cliquez sur "Nouveau" pour créer un nouveau Profile (initialisez-le avec "conventions Java").

Formateur Eclipse

Les deux étapes suivantes doivent être effectuées dans votre profil de formateur personnalisé.

Étape 2) Modifiez la configuration de l'indentation pour les lignes enveloppées

Configuration de l'identation

La modification permet d'utiliser des espaces blancs au lieu de tabulations.
Cela importera à l'étape suivante lorsque nous configurerons la stratégie d'encapsulation de ligne avec l'option d'indentation de colonne.
Il évitera en effet crée des espaces désagréables.

Étape 3) Modifiez l'indentation par défaut pour les lignes encapsulées et la stratégie d'encapsulation de ligne pour une invocation qualifiée

Ligne enveloppée taille


Voici une mise en forme de test avec le code de la question.

Avant la mise en forme :

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

Après formatage:

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

Création de modèles de code avec //@formatter:off ... //@formatter:on pour les cas d'angle.

Écrire manuellement ou copier-coller //@formatter:on et //@formatter:off est fine, comme vous l'écrivez rarement.
Mais si vous devez l'écrire plusieurs fois par semaine ou pire encore par jour, un une manière plus automatique est la bienvenue.

Étape 1) Aller à l'Éditeur Java modèle

Preferences menu et dans l'arbre, allez à la Java ->Editor -> Template.
entrez la description de l'image ici

Étape 2) Créer un modèle pour désactiver le formatage du code sélectionné

Modèle de Formatage de rabais sur

Vous pouvez maintenant le tester.
Sélectionnez les lignes dont vous souhaitez désactiver le formatage.
Maintenant, entrez ctrl+space deux fois (le premier est "Java proposals" et le second est " template proposition").
Vous devriez obtenir quelque chose comme :

modèle de proposition

Sélectionnez le modèle fmt comme dans la capture d'écran et cliquez sur "Entrée". Fait!

résultat après l'application du modèle

 7
Author: davidxxx, 2017-12-18 11:07:08