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.
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.
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.
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.
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();
Plans.Lorsque le nombre de paramètres est trop important et que la plupart d'entre eux sont généralement utilisés par défaut, 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 = (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.