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 OverflowI like using Optional<> to return values that may be null but today I saw people using them to accept parameters that may be null, if the parameter wasn't an Optional<> then it couldn't be null. I personally don't like doing that but I'd like to hear some opinions.
They were doing things like
double getStartingBalance(@NotNull Optional<UUID> user) { ... }
In my opinion, Optional<> is used to force the developer to handle null values. If my method is called and may return null, I'll return an Optional<> to make sure that null is handled but if I design the method and define that something cannot be null I can just use Objects#requereNonNull or if it can be null a simple check does the job. Also having to box values in optionals and having to use Optional.empty() for nulls to call a method is messy and makes the call harder to read.
So what are your opinions on using optionals as parameters?
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.
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);
}
Videos
If you have few parameters or very few sensible combinations of those parameters go for multiple overloads of the method with different parameter sets. If this generates a substantial number (more than 3-4), go for a config object instead
Why? The number of necessary overloads increases with quadratically with the number of parameters, therefore already even a reasonable set of parameters (like 5) will end up with tons of overloaded methods
The general answers would be to wrap the parameters either in a class/struct (depending on language) or a list (over which varargs is syntactic sugar). The reason is that when looking at a signature for the method/function, large numbers of parameters become harder and harder to follow.
Note that the composite data type doesn't really fix this, it just hides the problem a little bit. Generally speaking, methods/functions with large numbers of parameters are a design smell, a sign that it might be time to tweak the approach being used.
Whether something is "OK to use" depends on intent, team coding practices, common programming idioms, and how strictly you want to adhere to API guidelines.
First, the facts about Optional<T>:
A container object which may or may not contain a non-null value.
...
API Note:
Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.
Source: Oracle Java 11 JDK documentation (emphasis, mine)
Not a lot of concrete guidance here beyond the recommendation that Optional<T> be used as a return type. In a strict sense, you are not using this as a return type, therefore the answer is "no, this is not OK." On the other hand, you are using it as the type for a field which may contain null. So you seem to satisfy the first part.
But it works.
That's why it's important to remember these are guidelines. The mechanics of whether you can use something are a different discussion, and hence, your question. Instead, I like to see how these optional fields are being used to ensure a more suitable OO design doesn't already exist.
If I had to look at a codebase that was peppered with field.ifPresent(foo -> foo.method()) every time field was used, I would start to wonder if we have a design problem with field rather than it's type. Even in cases where you have field.isPresentOrElse(foo -> foo.method(), () -> something else), I still think we are missing something. Frankly, the constant calls to the Optional<T> methods as a round-about way to avoid NullPointerExceptions makes me think the Null Object Pattern might be a better choice here. Every method in the "null object" can be a no-op. Getters can return pre-canned values (whatever the "or else" part of Optional.ifPresentOrElse() would have returned).
I don' like optional dependencies because they can be null, so now I need to incessantly check for null, or I need to use some other non-idiomatic construct like Optional<T> with a bunch of pass-through method calls in lieu of null-checks.
Code is much cleaner if you can simply call field.method() rather than field.ifPresent(foo -> foo.method()).
Some additional notes:
The guidance in this answer applies best to situations where the
Tis a complex type. WhenTis a string, boolean, or numeric type, then the "optional" part of anOptional<T>becomes meaningful. AnOptional<boolean>can be true, false, or not present, which your application could interpret as "not specified." In that case, document this fact on the field.- In the case of a String, consider defaulting to an empty string instead. No need for null if you have proper encapsulation. A constructor or setter can enforce the "empty string instead of null" constraint.
When
Tis a complex type, consider other, more straight-forward designs first before resorting to anOptional<T>, if for no other reason than the Principle of Least Astonishment.Choose code that is easy to read first. In situations like this, I like to write the same code multiple ways. Consider writing it when checking for nulls, using another more suitable design pattern (like Null Object Pattern), and using an optional. Same code. 3 different styles. Which one do you like?
- And then present the same 3 styles to a colleague to see which one they like.
In the end, you get working code either way. Personal taste, other object-oriented design practices and team habits play a bigger role in this decision than API guidance.
Optional is now intended to be used as a Monad. Java designers effectively admitted that by adding Optional.stream() method.
Ignore earlier discussions about:
- serialization. Serialization should not be used in new code
- library interfaces. They are not different from your own.
- method argument. Using Optional as argument in much more idiomatic than doing a conditional dispatch over method overloads.
Surprisingly, important reasons not to use an Optional as a field are:
- memory consumption - it increases the size of your field up to 4 times.
- performance - it introduces additional memory indirection
These are only applicable to long-lived numerous objects (example: gamedev).