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 OverflowHow do I use optional parameters in Java? - Stack Overflow
How do I make parameters optional?
java - Are Optional fields okay? - Software Engineering Stack Exchange
Why exactly does Brian Goetz not recommend the use of Optional everywhere a value could be null?
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);
}
I'm writing a class for a drill. My main constructor has 4 parameters, but I want the last one to be optional, such that it's not a syntax error if only do three arguments when I create an object in the test class. Here's the method:
private int height;
private int width;
private int depth;
private String builder = null;
public Box(int wide, int high, int deep, String builtBy)
{
width = wide;
height = high;
depth = deep;
builder = builtBy;
}I want "builder" to return null for if I don't reassign it in the object creation. I thought this would happen automatically, but it says "Expected 4 arguments and found 3".
How do I do this?
EDIT: The solution is to create a second constructor with only three parameters
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).