Paramètres facultatifs Java

813

Comment utiliser les paramètres facultatifs en Java? Quelle spécification prend en charge les paramètres facultatifs?

Mike Pone
la source

Réponses:

517

varargs pourrait le faire (en quelque sorte). En dehors de cela, toutes les variables de la déclaration de la méthode doivent être fournies. Si vous voulez qu'une variable soit facultative, vous pouvez surcharger la méthode en utilisant une signature qui ne nécessite pas le paramètre.

private boolean defaultOptionalFlagValue = true;

public void doSomething(boolean optionalFlag) {
    ...
}

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}
laginimaïneb
la source
La surcharge de méthode est une bonne réponse à mon avis, alors que varargs est une très mauvaise réponse. Veuillez supprimer le commentaire sur les varargs ou expliquer le grave inconvénient causé par la possibilité de plusieurs valeurs de retour.
Andreas Vogl
L'ajout de variables globales peut entraîner d'autres problèmes.
Helen Cui
1654

Il existe plusieurs façons de simuler des paramètres facultatifs en Java:

  1. Surcharge de méthode.

    void foo(String a, Integer b) {
        //...
    }
    
    void foo(String a) {
        foo(a, 0); // here, 0 is a default value for b
    }
    
    foo("a", 2);
    foo("a");
    

    L'une des limites de cette approche est qu'elle ne fonctionne pas si vous avez deux paramètres facultatifs du même type et que l'un d'eux peut être omis.

  2. Varargs.

    a) Tous les paramètres facultatifs sont du même type:

    void foo(String a, Integer... b) {
        Integer b1 = b.length > 0 ? b[0] : 0;
        Integer b2 = b.length > 1 ? b[1] : 0;
        //...
    }
    
    foo("a");
    foo("a", 1, 2);
    

    b) Les types de paramètres facultatifs peuvent être différents:

    void foo(String a, Object... b) {
        Integer b1 = 0;
        String b2 = "";
        if (b.length > 0) {
          if (!(b[0] instanceof Integer)) { 
              throw new IllegalArgumentException("...");
          }
          b1 = (Integer)b[0];
        }
        if (b.length > 1) {
            if (!(b[1] instanceof String)) { 
                throw new IllegalArgumentException("...");
            }
            b2 = (String)b[1];
            //...
        }
        //...
    }
    
    foo("a");
    foo("a", 1);
    foo("a", 1, "b2");
    

    Le principal inconvénient de cette approche est que si les paramètres facultatifs sont de types différents, vous perdez la vérification de type statique. De plus, si chaque paramètre a une signification différente, vous avez besoin d'un moyen de les distinguer.

  3. Nulls. Pour remédier aux limites des approches précédentes, vous pouvez autoriser les valeurs nulles, puis analyser chaque paramètre dans un corps de méthode:

    void foo(String a, Integer b, Integer c) {
        b = b != null ? b : 0;
        c = c != null ? c : 0;
        //...
    }
    
    foo("a", null, 2);
    

    Maintenant, toutes les valeurs des arguments doivent être fournies, mais celles par défaut peuvent être nulles.

  4. Classe facultative. Cette approche est similaire aux valeurs null, mais utilise la classe facultative Java 8 pour les paramètres qui ont une valeur par défaut:

    void foo(String a, Optional<Integer> bOpt) {
        Integer b = bOpt.isPresent() ? bOpt.get() : 0;
        //...
    }
    
    foo("a", Optional.of(2));
    foo("a", Optional.<Integer>absent());
    

    Facultatif rend explicite un contrat de méthode pour un appelant, cependant, on peut trouver une telle signature trop verbeuse.

    Mise à jour: Java 8 inclut la classe java.util.Optionalprête à l'emploi, il n'est donc pas nécessaire d'utiliser la goyave pour cette raison particulière dans Java 8. Le nom de la méthode est cependant un peu différent.

  5. Modèle de générateur. Le modèle de générateur est utilisé pour les constructeurs et est implémenté en introduisant une classe de générateur distincte:

     class Foo {
         private final String a; 
         private final Integer b;
    
         Foo(String a, Integer b) {
           this.a = a;
           this.b = b;
         }
    
         //...
     }
    
     class FooBuilder {
       private String a = ""; 
       private Integer b = 0;
    
       FooBuilder setA(String a) {
         this.a = a;
         return this;
       }
    
       FooBuilder setB(Integer b) {
         this.b = b;
         return this;
       }
    
       Foo build() {
         return new Foo(a, b);
       }
     }
    
     Foo foo = new FooBuilder().setA("a").build();
    
  6. Plans. Lorsque le nombre de paramètres est trop grand et que la plupart des valeurs par défaut sont généralement utilisées, vous pouvez passer des arguments de méthode sous forme de mappage de leurs noms / valeurs:

    void foo(Map<String, Object> parameters) {
        String a = ""; 
        Integer b = 0;
        if (parameters.containsKey("a")) { 
            if (!(parameters.get("a") instanceof Integer)) { 
                throw new IllegalArgumentException("...");
            }
            a = (Integer)parameters.get("a");
        }
        if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>of(
        "a", "a",
        "b", 2, 
        "d", "value")); 
    

    En Java 9, cette approche est devenue plus simple:

        @SuppressWarnings("unchecked")
        static <T> T getParm(Map<String, Object> map, String key, T defaultValue)
        {
            return (map.containsKey(key)) ? (T) map.get(key) : defaultValue;
        }
    
        void foo(Map<String, Object> parameters) {
            String a = getParm(parameters, "a", "");
            int b = getParm(parameters, "b", 0);
            // d = ...
        }
    
        foo(Map.of("a","a",  "b",2,  "d","value"));
    

Veuillez noter que vous pouvez combiner n'importe laquelle de ces approches pour obtenir un résultat souhaitable.

Vitalii Fedorenko
la source
2
@Vitalii Fedorenko Hmm, je pense que vous avez fait une légère erreur de copier-coller dans # 6: vous effectuez une vérification de type pour un entier bien que votre avariable soit une chaîne (le cast est correct).
Unknown Id
5
@Aetos, votre lien est mort.
Robino
2
Lien alternatif @Robino soutenant que Facultatif ne doit pas être utilisé comme paramètre ici . Malgré l'utilisation de Optionalas comme paramètre dans l'une des options, cette réponse est très bonne.
Dherik
La solution la plus courante au problème ci-dessus est probablement la surcharge de méthode. Bien que je n'aie jamais vraiment été fan de ça. Personnellement, je préférerais qu'ils ajoutent une sorte d'identifiant pour les paramètres de configuration.
Alexander Heim
1
cela devrait être probablement la meilleure réponse - couvre tout
xproph
104

Il existe des paramètres facultatifs avec Java 5.0. Déclarez simplement votre fonction comme ceci:

public void doSomething(boolean... optionalFlag) {
    //default to "false"
    //boolean flag = (optionalFlag.length >= 1) ? optionalFlag[0] : false;
}

vous pouvez appeler avec doSomething();ou doSomething(true);maintenant.

bhoot
la source
13
C'est en fait la bonne réponse. C'est simple et compact. N'oubliez pas que vous pouvez obtenir plusieurs paramètres afin que Java les place dans un tableau. Par exemple, pour récupérer un seul paramètre, vous devez d'abord vérifier le contenu du tableau: 'code' boolean flag = (optionalFlag.length <1)? False: optionalFlag [0];
Salvador Valence
46
Non, ce n'est pas la bonne réponse, car cela n'autorise pas un seul paramètre facultatif, mais n'importe quel nombre de paramètres facultatifs. Bien que cela soit proche de ce que souhaite OP, ce n'est pas la même chose. C'est une source potentielle de bugs et de malentendus pour accepter plus de paramètres que nécessaire.
sleske
2
De cette façon, cela fonctionne si vous n'avez qu'un seul paramètre facultatif, mais vous devrez vérifier la longueur du tableau, etc. donc ce n'est pas super propre: | Si vous voulez un paramètre optionnel "un paramètre", vous pouvez tout aussi bien déclarer deux méthodes (une surchargée, doSomething() { doSomething(true); }pas de tableaux à traiter, pas d'ambiguïté
rogerdpack
S'il existe plusieurs paramètres facultatifs, cela ne fonctionne que s'ils sont tous du même type de données. Eh bien, vous pourriez faire le type Object, mais ça devient vraiment moche.
Jay
Comme l'a noté @sleske, l'utilisation de varargs a le terrible inconvénient de permettre le passage de plusieurs valeurs. OP a clairement demandé une solution pour "facultatif", ce qui signifie soit une valeur, soit zéro. Comment cela peut être considéré comme une bonne réponse par certains, c'est hors de moi.
Andreas Vogl
99

Vous pouvez utiliser quelque chose comme ceci:

public void addError(String path, String key, Object... params) { 
}

La paramsvariable est facultative. Il est traité comme un tableau d'objets nullable.

Étrangement, je n'ai rien trouvé à ce sujet dans la documentation, mais ça marche!

C'est "nouveau" dans Java 1.5 et au-delà (non pris en charge dans Java 1.4 ou version antérieure).

Je vois que l'utilisateur bhoot l'a mentionné aussi ci-dessous.

theninjagreg
la source
55

Malheureusement, Java ne prend pas directement en charge les paramètres par défaut.

Cependant, j'ai écrit un ensemble d'annotations JavaBean, et l'une d'entre elles prend en charge les paramètres par défaut comme les suivants:

protected void process(
        Processor processor,
        String item,
        @Default("Processor.Size.LARGE") Size size,
        @Default("red") String color,
        @Default("1") int quantity) {
    processor.process(item, size, color, quantity);
}
public void report(@Default("Hello") String message) {
    System.out.println("Message: " + message);
}

Le processeur d'annotation génère les surcharges de méthode pour prendre correctement en charge cela.

Voir http://code.google.com/p/javadude/wiki/Annotations

Exemple complet sur http://code.google.com/p/javadude/wiki/AnnotationsDefaultParametersExample

Scott Stanchfield
la source
53

Il n'y a pas de paramètres facultatifs en Java. Ce que vous pouvez faire, c'est surcharger les fonctions, puis passer les valeurs par défaut.

void SomeMethod(int age, String name) {
    //
}

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}
Dario
la source
23

VarArgs et surcharge ont été mentionnés. Une autre option est un modèle Builder, qui ressemblerait à ceci:

 MyObject my = new MyObjectBuilder().setParam1(value)
                                 .setParam3(otherValue)
                                 .setParam6(thirdValue)
                                 .build();

Bien que ce modèle soit plus approprié lorsque vous avez besoin de paramètres facultatifs dans un constructeur.

Yishai
la source
je veux ajouter une dépendance de paramètre pour que.let disons que je veux définir param3 uniquement si je définit param1. Par exemple. je veux définir le message de progression uniquement si je règle la progression visible. isProgressVisible (). setProgressMessage ("chargement"). comment puis-je y parvenir?
Harshal Bhatt
14

Dans JDK> 1.5, vous pouvez l'utiliser comme ceci;

public class NewClass1 {

    public static void main(String[] args) {

        try {
            someMethod(18); // Age : 18
            someMethod(18, "John Doe"); // Age & Name : 18 & John Doe
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void someMethod(int age, String... names) {

        if (names.length > 0) {
            if (names[0] != null) {
                System.out.println("Age & Name : " + age + " & " + names[0]);
            }
        } else {
            System.out.println("Age : " + age);
        }
    }
}
az3
la source
7

Vous pouvez faire quelque chose en utilisant une surcharge de méthode comme celle-ci.

 public void load(String name){ }

 public void load(String name,int age){}

Vous pouvez également utiliser l'annotation @Nullable

public void load(@Nullable String name,int age){}

passez simplement null comme premier paramètre.

Si vous passez une variable de même type, vous pouvez utiliser ceci

public void load(String name...){}
Ishan Fernando
la source
7

Version courte :

En utilisant trois points :

public void foo(Object... x) {
    String first    =  x.length > 0 ? (String)x[0]  : "Hello";
    int duration    =  x.length > 1 ? Integer.parseInt((String) x[1])     : 888;
}   
foo("Hii", ); 
foo("Hii", 146); 

(basé sur la réponse de @ VitaliiFedorenko)

T.Todua
la source
2

La surcharge est très bien, mais s'il y a beaucoup de variables qui nécessitent une valeur par défaut, vous vous retrouverez avec:

public void methodA(A arg1) {  }
public void methodA( B arg2,) {  }
public void methodA(C arg3) {  }
public void methodA(A arg1, B arg2) {  }
public void methodA(A arg1, C arg3) {  }
public void methodA( B arg2, C arg3) {  }
public void methodA(A arg1, B arg2, C arg3) {  }

Je suggère donc d'utiliser l'argument variable fourni par Java. Voici un lien pour l'explication.

Jigar Shah
la source
l'utilisation de varargs est considérée comme une mauvaise pratique. si le système a besoin de telles méthodes (mentionnées ci-dessus), alors vous devriez penser à une nouvelle conception car la conception de la classe semble mauvaise.
Diablo
2
Il est encore pire de trouver des excuses pour les déficiences Java et d'accuser les autres de mauvaises pratiques pour avoir remarqué ces inconvénients et avoir conçu des solutions de contournement.
BrianO
1
Veuillez ajouter toutes les informations pertinentes à votre réponse au lieu de créer un lien vers des sources externes - le lien donné est mort
Nico Haase
2

Vous pouvez utiliser une classe qui fonctionne un peu comme un générateur pour contenir vos valeurs facultatives comme celle-ci.

public class Options {
    private String someString = "default value";
    private int someInt= 0;
    public Options setSomeString(String someString) {
        this.someString = someString;
        return this;
    }
    public Options setSomeInt(int someInt) {
        this.someInt = someInt;
        return this;
    }
}

public static void foo(Consumer<Options> consumer) {
    Options options = new Options();
    consumer.accept(options);
    System.out.println("someString = " + options.someString + ", someInt = " + options.someInt);
}

Utilisez comme

foo(o -> o.setSomeString("something").setSomeInt(5));

La sortie est

someString = something, someInt = 5

Pour ignorer toutes les valeurs facultatives, vous devez l'appeler comme foo(o -> {});ou si vous préférez, vous pouvez créer une deuxième foo()méthode qui ne prend pas les paramètres facultatifs.

En utilisant cette approche, vous pouvez spécifier des valeurs facultatives dans n'importe quel ordre sans aucune ambiguïté. Vous pouvez également avoir des paramètres de différentes classes contrairement aux varargs. Cette approche serait encore meilleure si vous pouvez utiliser des annotations et la génération de code pour créer la classe Options.

Rybai
la source
1

Java prend désormais en charge les options dans la version 1.8, je suis coincé avec la programmation sur Android, donc j'utilise des valeurs nulles jusqu'à ce que je puisse refactoriser le code pour utiliser des types facultatifs.

Object canBeNull() {
    if (blah) {
        return new Object();
    } else {
        return null;
    }
}

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}
Pastille
la source
pouvez-vous fournir un exemple s'il vous plaît?
sepehr
1

Les arguments par défaut ne peuvent pas être utilisés en Java. Où en C #, C ++ et Python, nous pouvons les utiliser.

En Java, nous devons utiliser 2 méthodes (fonctions) au lieu d'une avec des paramètres par défaut.

Exemple:

Stash(int size); 

Stash(int size, int initQuantity);

http://parvindersingh.webs.com/apps/forums/topics/show/8856498-java-how-to-set-default-parameters-values-like-c-

Parvinder Singh
la source
28
Les versions plus récentes de C # autorisent les paramètres par défaut.
Sogger
2
java a également l'option "..." pour n'importe quel paramètre.
Cacho Santa
0

C'est une vieille question peut-être même avant l'introduction du type facultatif réel, mais de nos jours, vous pouvez considérer quelques choses: - utiliser la surcharge de méthode - utiliser le type facultatif qui a l'avantage d'éviter de passer des NULL autour Le type facultatif a été introduit dans Java 8 avant qu'il ne soit généralement utilisé à partir d'une bibliothèque tierce telle que Google's Guava. L'utilisation de paramètres / arguments facultatifs peut être considérée comme une surutilisation car le but principal était de l'utiliser comme heure de retour.

Réf: https://itcodehub.blogspot.com/2019/06/using-optional-type-in-java.html

xproph
la source
0

Nous pouvons faire des paramètres facultatifs par surcharge de méthode ou en utilisant DataType ...

| * | Surcharge de méthode:

RetDataType NameFnc(int NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(String NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(int NamePsgVar1, String NamePsgVar2)
{
    // |* Code Todo *|
    return RetVar;
}

Le moyen le plus simple est

| * | DataType ... peut être un paramètre facultatif

RetDataType NameFnc(int NamePsgVar, String... stringOpnPsgVar)
{
    if(stringOpnPsgVar.length == 0)  stringOpnPsgVar = DefaultValue; 

    // |* Code Todo *|
    return RetVar;
}

Sujay UN
la source
8
RetDtaTyp... sérieusement? Est-ce RetDataTypetrop difficile à taper?
Alexander - Reinstate Monica
Pouvez-vous ajouter quelques explications supplémentaires à ce code? Que font toutes ces variables étranges?
Nico Haase
Changement des noms de variables pour faciliter la compréhension ...
Sujay UN
0

Si vous prévoyez d'utiliser une interface avec plusieurs paramètres , vous pouvez utiliser le modèle structurel suivant et implémenter ou remplacer apply - une méthode basée sur vos besoins .

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}
Mukundhan
la source
0

S'il s'agit d'un point de terminaison API, une méthode élégante consiste à utiliser des annotations "Spring":

@GetMapping("/api/foos")
@ResponseBody
public String getFoos(@RequestParam(required = false, defaultValue = "hello") String id) { 
    return innerFunc(id);
}

Notez dans ce cas que le innerFunc aura besoin de la variable, et comme ce n'est pas un point de terminaison api, ne peut pas utiliser cette annotation Spring pour la rendre facultative. Référence: https://www.baeldung.com/spring-request-param

Sophie Cooperman
la source