web-dev-qa-db-fra.com

Java 8: Mise en forme de lambda avec des nouvelles lignes et une indentation

Ce que je voudrais réaliser avec l'indentation lambda est la suivante:

Déclaration 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);

Relevé monoligne:

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



Actuellement, Eclipse est en train de formater automatiquement les éléments suivants:

Déclaration 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);

Relevé monoligne:

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 l'appel return et qu'il n'y a pas d'espace entre les deux. Je préférerais que je puisse commencer le lambda dans une nouvelle ligne en retrait, et que le .filter( _ appel serait juste au-dessus du .collect( appel. Cependant, la seule chose qui peut être personnalisée avec Java-8 Eclipse Formatter standard est le corset au début du corps lambda, mais rien pour le () entre parenthèses, ni l’indentation.

Et dans le cas d'appels sur une seule ligne, il utilise simplement le retour à la ligne de base et en fait un désordre enchaîné. Je ne pense pas avoir besoin d'expliquer pourquoi c'est difficile à déchiffrer après.

Existe-t-il un moyen de personnaliser davantage la mise en forme et d'obtenir le premier type de mise en forme dans Eclipse? (Ou, éventuellement, dans un autre IDE comme IntelliJ IDEA.)



EDIT: Le plus proche que je pouvais obtenir était IntelliJ IDEA 13 Community Edition (lire: édition gratuite: P)), qui était la suivante (définie par une 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);    

Cela permet également "d'aligner" l'appel de 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 même si cela a plus de sens, la deuxième version le pousse trop loin, alors 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 est raisonnable, mais j'ai peut-être mal gaché quelque chose, de sorte que des ajustements mineurs sont nécessaires.

Si vous êtes hongrois comme moi et que vous utilisez une mise en page hongroise, alors ce clavier peut vous être utile afin que vous ne puissiez pas 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 la première ligne de la lambda dans une nouvelle ligne, sinon c'est une façon assez raisonnable de formater, donc je marquerai ceci comme accepté.

72
EpicPandaForce

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

Et appliquez ensuite le formateur automatique (sans modification):

// 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 déclaration de ligne unique. D'après mon expérience, IntelliJ est plus flexible dans l'application de sa mise en forme automatique. IntelliJ est moins susceptible de supprimer ou d’ajouter des retours à la ligne. Si vous le mettez ici, cela suppose que vous avez l’intention de le mettre là. IntelliJ se fera un plaisir d'ajuster votre espace de tabulation pour vous.


IntelliJ peut également être configuré pour effectuer certaines de ces tâches à votre place. Sous "Paramètres" -> "Style de code" -> "Java", dans l'onglet "Habillage et accolades", vous pouvez définir "Appels de méthode en chaîne" sur "Emballer 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());
24
Mike Rylander

Dans Eclipse, pour les instructions sur une seule ligne:

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

52
Arend

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

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

enter image description here

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

enter image description here

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
19
Diego D

Je formate une déclaration sur 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());
14
Saljack

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 pour la plupart des cas d'utilisation.
Cela peut convenir à certains, mais déplaisant 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.

Notez que mon chemin 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 boucler la ligne au lieu d’utiliser 1 invocation par défaut.

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

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

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


Personnalisation de la configuration du formateur Eclipse
La valeur à modifier est entourée en rouge dans la capture.

Étape 1) Créez votre propre Java Code Style Formatter

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

Eclipse formatter

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

Étape 2) Modifier la configuration d'indentation pour les lignes enveloppées

Identation configuration

La modification permet d’utiliser des espaces blancs au lieu de tabulations.
Cela aura une importance dans la prochaine étape puisque nous configurons la politique de retour à la ligne avec l'option d'indentation de colonne.
Cela évitera en effet de créer des espaces désagréables.

Étape 3) Modifier l'indentation par défaut pour les lignes encapsulées et la stratégie d'habillage de ligne pour un appel qualifié

Wrapped line size


Voici un test de formatage avec le code de la question.

Avant de formater:

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 le 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 angles.

Écrire manuellement ou copier-coller //@formatter:on et //@formatter:off va bien car vous écrivez rarement.
Mais si vous devez l’écrire plusieurs fois par semaine, voire pire le jour, une méthode plus automatique est la bienvenue.

Étape 1) Accédez à Java).

Preferences menu et dans l'arborescence, allez à Java ->Editor -> Template.
enter image description here

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

Template Formatter off on

Vous pouvez maintenant le tester.
Sélectionnez les lignes pour lesquelles vous souhaitez désactiver le formatage.
Maintenant entrez ctrl+space deux fois (le premier est "propositions Java" et le second est "propositions modèles").
Vous devriez obtenir quelque chose comme:

template proposal

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

result after template application

12
davidxxx

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> Code Style> Formateur". Cliquez sur le bouton "Modifier ...", allez à l'onglet "Balises Désactiver/Activer" et activez les balises.

7
throp