Java doesn't support optional parameters. However, you can simulate using the following methods: Method overloading.
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");
One of the constraints of this method is that it doesn't work if you have two arbitrary parameters of the same type and any of them can be neglected. Varargs. a) All optional parameters are of the identical 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) Types of optional parameters may vary:
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");
The main disadvantage of this method is that if optional parameters are of different types you lose static type checking. Moreover, if each parameter has a distinct meaning you need some way to distinguish them.
Nulls: To address the constraints of the earlier methods you can allow null values and then analyze each parameter in a method body:
void foo(String a, Integer b, Integer c) { b = b != null ? b : 0; c = c != null ? c : 0; //... } foo("a", null, 2);
Now all arguments values must be given, but the default ones may be null.
Optional class. This method is similar to null, but uses Java 8 Optional class for parameters that have a default value:
void foo(String a, Optional<Integer> bOpt) { Integer b = bOpt.isPresent() ? bOpt.get() : 0; //... } foo("a", Optional.of(2)); foo("a", Optional.<Integer>absent()); This makes a method contract explicit for a caller, however, one may find such signature too verbose. Builder pattern. The builder pattern is used for constructors and is performed by introducing a separate Builder
class: 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();
Maps. When the quantity of parameters is too large and for most of the default values are usually used, you can pass method arguments as a map of their names/values:
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"));
Please note that you can merge any of these methods to achieve a desirable result.