There are several ways to simulate optional parameters in Java.
Method overloading
Copyvoid 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 limitations of this approach is that it doesn't work if you have two optional parameters of the same type and any of them can be omitted.
Varargs
a) All optional parameters are of the same type:
Copyvoid 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 be different:
Copyvoid 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 drawback of this approach is that if optional parameters are of different types you lose static type checking. Furthermore, if each parameter has the different meaning you need some way to distinguish them.
Nulls
To address the limitations of the previous approaches you can allow null values and then analyze each parameter in a method body:
Copyvoid 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 provided, but the default ones may be null.
Optional class
This approach is similar to nulls, but uses Java 8 Optional class for parameters that have a default value:
Copyvoid foo(String a, Optional<Integer> bOpt) {
Integer b = bOpt.isPresent() ? bOpt.get() : 0;
//...
}
foo("a", Optional.of(2));
foo("a", Optional.<Integer>absent());
Optional makes a method contract explicit for a caller, however, one may find such signature too verbose.
Update: Java 8 includes the class java.util.Optional out-of-the-box, so there is no need to use guava for this particular reason in Java 8. The method name is a bit different though.
Builder pattern
The builder pattern is used for constructors and is implemented by introducing a separate Builder class:
Copyclass 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 number 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:
Copyvoid 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"));
In Java 9, this approach became easier:
Copy@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"));
Please note that you can combine any of these approaches to achieve a desirable result.
Answer from Vitalii Fedorenko on Stack OverflowThere are several ways to simulate optional parameters in Java.
Method overloading
Copyvoid 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 limitations of this approach is that it doesn't work if you have two optional parameters of the same type and any of them can be omitted.
Varargs
a) All optional parameters are of the same type:
Copyvoid 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 be different:
Copyvoid 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 drawback of this approach is that if optional parameters are of different types you lose static type checking. Furthermore, if each parameter has the different meaning you need some way to distinguish them.
Nulls
To address the limitations of the previous approaches you can allow null values and then analyze each parameter in a method body:
Copyvoid 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 provided, but the default ones may be null.
Optional class
This approach is similar to nulls, but uses Java 8 Optional class for parameters that have a default value:
Copyvoid foo(String a, Optional<Integer> bOpt) {
Integer b = bOpt.isPresent() ? bOpt.get() : 0;
//...
}
foo("a", Optional.of(2));
foo("a", Optional.<Integer>absent());
Optional makes a method contract explicit for a caller, however, one may find such signature too verbose.
Update: Java 8 includes the class java.util.Optional out-of-the-box, so there is no need to use guava for this particular reason in Java 8. The method name is a bit different though.
Builder pattern
The builder pattern is used for constructors and is implemented by introducing a separate Builder class:
Copyclass 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 number 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:
Copyvoid 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"));
In Java 9, this approach became easier:
Copy@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"));
Please note that you can combine any of these approaches to achieve a desirable result.
varargs could do that (in a way). Other than that, all variables in the declaration of the method must be supplied. If you want a variable to be optional, you can overload the method using a signature which doesn't require the parameter.
Copyprivate boolean defaultOptionalFlagValue = true;
public void doSomething(boolean optionalFlag) {
...
}
public void doSomething() {
doSomething(defaultOptionalFlagValue);
}
I agree that you should go with what makes more sense semantically, but I also find that, when the boolean argument is optional, the thing that fairly often makes more sense when omitting it is for it to have a default value of false - in languages that initialize instances of primitive types, false is generally the default value for a boolean.
It then acts as an "on" flag for the less commonly used feature. So that you have:
DoSomething(someParams);
DoSomething(someParams, true);
// Or, if your language supports named parameters
DoSomething(someParams, optionalFeature: true);
So, I would have a slight bias for the parameter to default to false, unless doing so feels unnatural (as in, it feels unnatural as a sentence when you read it). Sometimes, the exceptional behavior is better conceptualized as turning something off (for example, setters that take a boolean argument are often constructed this way, see Joop Eggen's answer; however, you should strive for encapsulation, and minimize the use of getters and setters).
If the feature controlled by the boolean argument is not less commonly used (in the general case), I wouldn't make it optional. Design for the general case. If there's a small number of users with applications that mostly use one value for the argument, they can create their own wrapper.
As for the logic inside the method, if it turns out that your choice makes it awkward to write conditionals, just introduce a new well-named variable with the flipped value (or a new variable that summarizes several boolean checks that repeat, or whatever) - it's not going to be a problem if your method is fairly short (which it should be - you shouldn't have a sprawling tree of nested if-statements in there).
In line with @Ewan's arguments, choose the one making the code easier to read and reason about.
For example, double negations (!isNotA or Ewan's !FileDoesntExist) are the kind of conditional statements you want to avoid because they cause a cognitive burden. Unbearable when flag names are meaningless.
Negation involves an extra cognitive step (even when the negation is in the name of the flag). We usually think first in the is part and then negate it. So, the more your if blocks look like the following, the better
//only for illustration
if(isA && isB)
else (isA) //isA && !isB
else (isB) //!isA && isB
else //!isA && !isB
Ok, this is easier to say than done, but think about it. It depends on how we translate (mentally) rules into conditions. If we think in terms of "is not", we will end up with flags prefixed with isNot, and it's ok if we can address it later. For instance, we can invert the flag isA = !isNotA and use isA instead. 1
To summarize, use the notation that makes your code easier to read (to me, affirmative statements), always use meaningful names and avoid negations and double negations as the plague; your teammates will appreciate it.
1: Do it only to make your conditionals easier to read and reason about. Don't systematically invert flags all over the code.
For good order
if (spouseIsMale.orElse(false)) {
System.out.println("There is a male spouse.");
}
Clear.
It is possible to achieve that behaviour with .filter(b -> b):
spouseIsMale.filter(b -> b).ifPresent(b -> System.out.println("There is a male spouse."));
However, it costs some brain execution time seconds to understand what is going on here.
Use Boolean class
public String foo(Boolean addWhere){
String query = "SELECT * FROM tbl";
if(addWhere != null){
query += (addWhere? " WHERE true" : " WHERE false");
}
return query;
}
Call it as following,
String q = foo(false);
You can use java.lang.Boolean; this is the wrapper class for primitive boolean. The value can be null, Boolean.TRUE or Boolean.FALSE, but due to auto-unboxing you can also directly compare with true and false.
Oh, those coding styles are to be taken with a bit of salt.
- (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
- (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
- (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
- (-) In comparison to nullable parameters Optional is more costly.
- (-) The risk of someone passing the Optional as null in actual parameters.
In general: Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.
The best post I've seen on the topic was written by Daniel Olszewski:
Although it might be tempting to consider Optional for not mandatory method parameters, such a solution pale in comparison with other possible alternatives. To illustrate the problem, examine the following constructor declaration:
Copypublic SystemMessage(String title, String content, Optional<Attachment> attachment) { // assigning field values }At first glance it may look as a right design decision. After all, we explicitly marked the attachment parameter as optional. However, as for calling the constructor, client code can become a little bit clumsy.
CopySystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty()); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));Instead of providing clarity, the factory methods of the Optional class only distract the reader. Note there’s only one optional parameter, but imagine having two or three. Uncle Bob definitely wouldn’t be proud of such code 😉
When a method can accept optional parameters, it’s preferable to adopt the well-proven approach and design such case using method overloading. In the example of the SystemMessage class, declaring two separate constructors are superior to using Optional.
Copypublic SystemMessage(String title, String content) { this(title, content, null); } public SystemMessage(String title, String content, Attachment attachment) { // assigning field values }That change makes client code much simpler and easier to read.
CopySystemMessage withoutAttachment = new SystemMessage("title", "content"); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", attachment);
Java does not support optional parameters in functions.
Instead, provide an overload to the function like this:
void myFunction(boolean parameter)
{
/*ToDo - code here*/
}
void myFunction()
{
myFunction(true/*i.e. call the overload with a default to true*/);
}
Of, course, more than one parameter can be defaulted in this way and you can have multiple overloads to support different default schemes.
As for the varargs-notation (boolean...): the varargs-parameter always has to be the last one, so you can only have one of these.
You can consider passing null for omitted parameters or you could try some sort of method-overloading like Bathseba suggested.
When going for the overloading you have to keep in mind that there are no named paramters, so only poosition and type can define which parameter is passed and which is omitted!!