web-dev-qa-db-fra.com

Java prend-il en charge les valeurs de paramètre par défaut?

Je suis tombé sur du code Java présentant la structure suivante:

public MyParameterizedFunction(String param1, int param2)
{
    this(param1, param2, false);
}

public MyParameterizedFunction(String param1, int param2, boolean param3)
{
    //use all three parameters here
}

Je sais qu'en C++, je peux attribuer une valeur par défaut à un paramètre. Par exemple:

void MyParameterizedFunction(String param1, int param2, bool param3=false);

Java supporte-t-il ce type de syntaxe? Y a-t-il des raisons pour lesquelles cette syntaxe en deux étapes est préférable?

1524
gnavi

Non, la structure que vous avez trouvée indique comment Java la gère (c'est-à-dire avec une surcharge au lieu des paramètres par défaut).

Pour les constructeurs, Voir Effective Java: Guide du langage de programmation Point 1 Conseil (Considérez les méthodes fabriques statiques au lieu des constructeurs) si la surcharge devient compliquée. Pour d’autres méthodes, renommer certains cas ou utiliser un objet paramètre peut aider. C'est lorsque vous avez suffisamment de complexité que la différenciation est difficile. Un cas précis est celui où vous devez différencier en utilisant l'ordre des paramètres, pas seulement le nombre et le type.

873
Kathy Van Stone

Non, mais vous pouvez utiliser le Générateur , comme décrit dans cette réponse de débordement de pile .

Comme décrit dans la réponse liée, le modèle de générateur vous permet d'écrire du code comme

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

dans lequel certains champs peuvent avoir des valeurs par défaut ou être facultatifs.

604
Eli Courtwright

Il existe plusieurs façons de simuler des paramètres par défaut en Java:

  1. Méthode surchargée.

    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 limitations des approches précédentes, vous pouvez autoriser les valeurs NULL 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 d'argument doivent être fournies, mais les valeurs par défaut peuvent être nulles.

  4. Classe facultative. Cette approche est similaire aux valeurs nulles, mais utilise Java 8. Classe facultative pour les paramètres ayant 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 un contrat de méthode explicite pour un appelant. Toutefois, cette signature peut être trop détaillée.

  5. Pattern Builder. Le pattern Builder est utilisé pour les constructeurs et est implémenté en introduisant une classe Builder séparée:

     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. Maps. Lorsque le nombre de paramètres est trop important et que, pour la plupart, les valeurs par défaut sont généralement utilisées, vous pouvez passer les 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 = (String)parameters.get("a");
        } else if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>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.

430

Malheureusement non.

234
Rob H

Malheureusement oui.

void MyParameterizedFunction(String param1, int param2, bool param3=false) {}

pourrait être écrit dans Java 1.5 comme suit:

void MyParameterizedFunction(String param1, int param2, Boolean... params) {
    assert params.length <= 1;
    bool param3 = params.length > 0 ? params[0].booleanValue() : false;
}

Mais si vous devez ou non dépendre de ce que vous pensez du compilateur générant une

new Boolean[]{}

pour chaque appel.

Pour plusieurs paramètres par défaut:

void MyParameterizedFunction(String param1, int param2, bool param3=false, int param4=42) {}

pourrait être écrit dans Java 1.5 comme suit:

void MyParameterizedFunction(String param1, int param2, Object... p) {
    int l = p.length;
    assert l <= 2;
    assert l < 1 || Boolean.class.isInstance(p[0]);
    assert l < 2 || Integer.class.isInstance(p[1]);
    bool param3 = l > 0 && p[0] != null ? ((Boolean)p[0]).booleanValue() : false;
    int param4 = l > 1 && p[1] != null ? ((Integer)p[1]).intValue() : 42;
}

Cela correspond à la syntaxe C++, qui autorise uniquement les paramètres par défaut à la fin de la liste de paramètres.

Au-delà de la syntaxe, il existe une différence entre le type d'exécution et le type C++ vérifiés lors de la compilation lors de l'exécution.

80
ebelisle

Non, mais vous pouvez très facilement les imiter. Qu'est-ce qui était en C++:

public: void myFunction(int a, int b=5, string c="test") { ... }

En Java, ce sera une fonction surchargée:

public void myFunction(int a, int b, string c) { ... }

public void myFunction(int a, int b) {
    myFunction(a, b, "test");
}

public void myFunction(int a) {
    myFunction(a, 5);
}

Il a été mentionné précédemment que les paramètres par défaut provoquaient des cas ambigus de surcharge de fonctions. Ce n'est tout simplement pas vrai, nous pouvons voir dans le cas du C++: oui, peut-être que cela peut créer des cas ambigus, mais ces problèmes peuvent être facilement résolus. Cela n’a tout simplement pas été développé en Java, probablement parce que les créateurs voulaient un langage beaucoup plus simple que le C++ - s’ils avaient raison, c’est une autre question. Mais la plupart d'entre nous ne pensons pas qu'il utilise Java en raison de sa simplicité.

36
peterh

Vous pouvez le faire dans Scala, qui fonctionne sur la machine virtuelle Java et est compatible avec les programmes Java. http://www.scala-lang.org/

c'est à dire.

class Foo(var prime: Boolean = false, val rib: String)  {}
22
lythic

Non, mais le moyen le plus simple de le mettre en œuvre est:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

    param3 = param3 == null ? false : param3;
}

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

o au lieu de l'opérateur ternaire , vous pouvez utiliser if:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

    if (param3 == null) {
        param3 = false;
    }
}

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}
12
simhumileco

Je pourrais peut-être énoncer une évidence ici, mais pourquoi ne pas simplement mettre en œuvre le paramètre "par défaut" vous-même?

public class Foo() {
        public void func(String s){
                func(s, true);
        }
        public void func(String s, boolean b){
                //your code here
        }
}

pour le défaut vous utiliseriez l'éther

func ("ma chaîne");

et si vous ne souhaitez pas utiliser la valeur par défaut, vous utiliserez

func ("ma chaîne", false);

11
IronWolf

Non. En général, Java n'a pas beaucoup de sucre syntaxique (aucun), car ils ont essayé de créer un langage simple.

6
tomjen

Comme Scala a été mentionné, Kotlin mérite également d'être mentionné. En Kotlin, les paramètres de fonction peuvent également avoir des valeurs par défaut et peuvent même faire référence à d'autres paramètres:

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
    ...
}

Comme Scala, Kotlin fonctionne sur la machine virtuelle Java et peut être facilement intégré à des projets Java existants.

6
mrts

Non.

Vous pouvez obtenir le même comportement en passant un objet avec des valeurs par défaut intelligentes. Mais encore une fois, cela dépend de ce que votre cas est à portée de main.

6
Santosh Gokak

Il n'est pas pris en charge, mais il existe plusieurs options, comme utiliser un modèle d'objet paramètre avec un sucre de syntaxe:

public class Foo() {
    private static class ParameterObject {
        int param1 = 1;
        String param2 = "";
    }

    public static void main(String[] args) {
        new Foo().myMethod(new ParameterObject() {{ param1 = 10; param2 = "bar";}});
    }

    private void myMethod(ParameterObject po) {
    }
}

Dans cet exemple, nous construisons ParameterObject avec des valeurs par défaut et les substituons dans la section d'initialisation d'instance de classe { param1 = 10; param2 = "bar";}

4
hoaz

Essayez cette solution:

public int getScore(int score, Integer... bonus)
{
    if(bonus.length > 0)
    {
        return score + bonus[0];
    }

    return score;
}
3
Hamzeh Soboh

Vous pouvez utiliser générateur de méthode Java pour générer automatiquement le générateur avec les valeurs par défaut.

Ajoutez simplement @GenerateMethodInvocationBuilder à la classe ou à l'interface, et @Default aux paramètres des méthodes pour lesquelles vous souhaitez obtenir des valeurs par défaut. Un générateur sera généré lors de la compilation, en utilisant les valeurs par défaut que vous avez spécifiées avec vos annotations.

@GenerateMethodInvocationBuilder
public class CarService {
 public CarService() {
 }

 public String getCarsByFilter(//
   @Default("Color.BLUE") Color color, //
   @Default("new ProductionYear(2001)") ProductionYear productionYear,//
   @Default("Tomas") String owner//
 ) {
  return "Filtering... " + color + productionYear + owner;
 }
}

Et ensuite, vous pouvez invoquer les méthodes.

CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
  .invoke(instance);

Ou définissez l'une des valeurs par défaut sur autre chose.

CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
  .withColor(Color.YELLOW)//
  .invoke(instance);
2
Tomas Bjerre

Une approche similaire à https://stackoverflow.com/a/13864910/2323964 qui fonctionne dans Java 8 consiste à utiliser une interface avec des getters par défaut. Ce sera plus un espace blanc commenté, mais c'est moqueur, et c'est génial quand vous avez un tas d'instances où vous voulez réellement attirer l'attention sur les paramètres.

public class Foo() {
    public interface Parameters {
        String getRequired();
        default int getOptionalInt(){ return 23; }
        default String getOptionalString(){ return "Skidoo"; }
    }

    public Foo(Parameters parameters){
        //...
    }

    public static void baz() {
        final Foo foo = new Foo(new Person() {
            @Override public String getRequired(){ return "blahblahblah"; }
            @Override public int getOptionalInt(){ return 43; }
        });
    }
}
2
Novaterata

J'ai maintenant passé pas mal de temps à comprendre comment utiliser cela avec des méthodes qui renvoient des valeurs, et je n'ai encore vu aucun exemple, j'ai pensé qu'il serait peut-être utile d'ajouter ceci ici:

int foo(int a) {
    // do something with a
    return a;
}

int foo() {
    return foo(0); // here, 0 is a default value for a
}
2
AAGD

Il y a une demi-douzaine ou mieux de problèmes comme celui-ci, vous finissez par arriver au modèle d'usine statique ... voir l'API crypto pour cela. Difficile à expliquer, mais réfléchissez de la manière suivante: si vous avez un constructeur, par défaut ou non, le seul moyen de propager un état au-delà des accolades est d’avoir un isValid booléen; (avec le constructeur null en tant que valeur par défaut v failed) ou émettez une exception qui n’est jamais informative lors de la récupération par les utilisateurs du champ.

Code Correct be damned, j'écris mille constructeurs de lignes et fais ce dont j'ai besoin. Je trouve l'utilisation de isValid lors de la construction d'un objet, c'est-à-dire deux constructeurs de lignes, mais pour une raison quelconque, je migre vers le modèle d'usine statique. Il me semble que vous pouvez faire beaucoup de choses si vous appelez une méthode, il y a toujours des problèmes avec sync () mais les valeurs par défaut peuvent être 'substituées' mieux (plus sûr)

Je pense que ce que nous devons faire ici est de régler le problème de null comme valeur par défaut vis-à-vis de quelque chose String one = new String (""); en tant que variable membre, puis vérifie la valeur null avant d'affecter la chaîne transmise au constructeur.

Très remarquable la quantité d'informatique brute, stratosphérique réalisée en Java.

C++ et ainsi de suite a les bibliothèques du vendeur, oui. Java peut les distancer sur des serveurs de grande taille grâce à sa boîte à outils massive. Étudiez les blocs d’initialisation statiques, restez avec nous.

1
Nicholas Jordan

Voici comment je l'ai fait ... ce n'est peut-être pas aussi pratique que d'avoir un "argument optionnel" par rapport à votre paramètre défini, mais le travail est fait:

public void postUserMessage(String s,boolean wipeClean)
{
    if(wipeClean)
    {
        userInformation.setText(s + "\n");
    }
    else
    {
        postUserMessage(s);
    }
}

public void postUserMessage(String s)
{
    userInformation.appendText(s + "\n");
}

Remarquez que je peux invoquer le même nom de méthode avec une simple chaîne ou avec une chaîne et une valeur booléenne. Dans ce cas, si vous définissez wipeClean sur true, tout le texte de TextArea sera remplacé par la chaîne fournie. Si wipeClean est défini sur false ou s'il est omis, il suffit d'ajouter le texte fourni à TextArea.

Notez également que je ne répète pas le code dans les deux méthodes, je rajoute simplement la possibilité de réinitialiser TextArea en créant une nouvelle méthode portant le même nom uniquement avec le booléen ajouté.

Je pense en fait que c'est un peu plus propre que si Java fournissait un "argument optionnel" pour nos paramètres, car nous aurions alors besoin de coder pour les valeurs par défaut, etc. Dans cet exemple, je n'ai pas à m'inquiéter aucun de cela. Oui, j’ai ajouté une autre méthode à ma classe, mais c’est plus facile à lire à la longue, à mon humble avis.

1
Michael Sims

NON, mais nous avons une alternative sous la forme de surcharge de fonctions.

appelé quand aucun paramètre n'est passé

void operation(){

int a = 0;
int b = 0;

} 

appelé quand "un" paramètre a été passé

void operation(int a){

int b = 0;
//code

} 

appelé lorsque le paramètre b est passé

void operation(int a , int b){
//code
} 
0
Umair Khalid