I have come across many questions and discussions about Java optional parameters! So I wrote this article to highlight and cover in-depth all the workaround methods used to implement and handle default arguments in Java.

If you are one of those who want to understand how optional parameters work in Java, then maybe this post is just written for you! Read on for all the details!

What is an optional parameter?

An optional parameter in Java, as the name implies, refers simply to a parameter that may be optional for a method invocation!

It describes the possibility to call a method without specifying some of the arguments used in its definition!

Parameters are variables that you can pass to a method or a function! When you invoke that method, you need to make sure that the type and the order of the arguments you want to pass must match the ones specified in the method declaration!

Does Java support optional parameters?

Unfortunately, Java by design does not support default parameters directly, either in methods or constructors, unlike other languages like Kotlin and Python!

With that being said, all parameters are required in Java! When you want to invoke a method, you have to specify all of the arguments which are used in its declaration!

How to make parameters optional in Java?

There are several strategies and approaches that can be used to simulate and implement Java default parameters!

Let’s take a closer look and explore all these approaches and options in details one by one.

Method overloading:

Overloading is a mechanism that allows to define multiple methods with the same name and different list of arguments! Simple, right ?

Depending upon the arguments passed, the compiler can choose the right version to call!

The idea of this approach is very simple: instead of providing default values for optional parameters, we can create multiple versions - in term of parameters - of the same method!

overloading method optional parameters

Each overloaded method must accept only arguments which are required! The example below shows how to implement optional parameters in Java using method overloading strategy!

    
        package com.azhwani.optional.params
        public class OptionalParams {
            public OptionalParams() { 
            }
            // First version of the method display() 
            public void display(int x) {
                System.out.println("First version : "+x);
            }
            // Second version of the method display() 
            public void display(int x, int y) { 
                System.out.println("Second version : "+x+" "+y);
            }
            // Second version of the method display() 
            public void display(int x, int y, int z) { 
                System.out.println("Third version  : "+x+" "+y +" "+z);
            }
            
            public static void  main(String[] args) {
                // TODO Auto-generated method stub
                OptionalParams obj = new  OptionalParams();
                // Demo to demonstrate Java optional parameters
                obj.display(2);
                obj.display(3,5);
                obj.display(1,2,4);
            }
        }
    

Java default parameters demo:

When you run the above example, the output will be:

 First version : 2 
 Second version : 3 5
 Third version  : 1 2 4

In the above example, we can see that the method optionalParams() is overloaded based on the number of parameters!

The cleanliness and the flexibility of this strategy, makes it one the most known way to implement Java optional arguments concept!

Also, it’s worth mentioning that one of the drawbacks of this approach, is the fact that overloading can quickly become difficult to maintain if the method has too many parameters to handle.

Telescoping constructor

Constructors are just like methods, they can also be overloaded so that each one performs one single responsibility. Cool!

Java does not provide real support for default values for constructor parameters, so Telescoping constructor is one of the available options to convey that some parameters are optional in Java!

The main idea behind Telescoping constructor concept is based on delegation: Each constructor calls another constructor which has more parameters!

Confused? let me explain! The constructor with one parameter calls the constructor with two parameters… you can chain your constructors until all optional arguments are covered to avoid code duplication!

    
        public class Student {
            private int Id;
            private String firstname;
            private String lastname;
            private String email;
            private int age;
            public Student(String firstname) {
                this(firstname,"Last name");
            }
            public Student(String firstname,String lastname) { 
                this(firstname,lastname,"email@email.com");
            }
            public Student(String firstname,String lastname,String email) { 
                this(firstname,lastname,email,20);
            }
            public Student(String firstname,String lastname,String email,int age) {
                this.firstname = firstname;
                this.lastname  = lastname;
                this.email     = email;
                this.age       = age;
            }
            
            public static void  main(String[] args) {
                Student sdt01 = new  Student("John");
                Student sdt02 = new  Student("Patricia","Edwards");
                Student sdt03 = new  Student("Donna","Patterson","donna.patterson@email.com");
            }
        }
    

As you can see, each constructor has different number of parameters, so basically, you have the possibility to create an instance with the combination of parameters you want!

This approach is very simple and easy to implement in case you have small number of parameters!

However, on the other hand, this option is considered an anti-pattern, constructors with many parameters are hard to read and maintain!

If you have 2 or 3 parameters with the same type, then you may accidentally switch their values without any compilation error!

JavaBeans:

In short, a JavaBean represents a simple Java class that follows certain specific conventions about method naming, construction and behaviour:

  • It must implement Serializable interface.

  • It must have a public no-arg constructor.

  • All properties must be declared private.

  • Each private property must have public getter and setter methods.

JavaBean convention suggests an easiest and scalable way to set optional parameters in Java! It allows to create an object using step-by-step strategy, first you need to call the default constructor to instantiate the class, and then call setXXX methods to set your object properties!

java bean

Now let’s see together an example of how a JavaBean is defined in Java:

    
        public class Employee  {
            private String firstname;
            private String lastname;
            private String email;
            private boolean ismanager;
            
            public Employee () {
            }
            public String getFirstname() {
                return firstname;
            }
            public void setFirstname(String firstname) {
                this.firstname = firstname;
            }
            public String getLastname() {
                return lastname;
            }
            public void setLastname(String lastname) {
                this.lastname = lastname;
            }
            public String getEmail() {
                return email;
            }
            public void setEmail(String email) {
                this.email = email;
            }
            public boolean isManager() {
                return ismanager;
            }
            public void setManager(boolean ismanager) {
                this.isManager = ismanager;
            }
        }
    

This approach has several advantages:

  • Easy to implement and change.

  • Easy to read and to scale - Adding more parameters is not a big deal! Most Java IDE can generate getters and setters for you!

However, there are also some drawbacks:

  • JavaBean is anti-pattern, it violates some of OOP basic principles.

  • It creates an empty object (default constructor) with an inconsistent state which means the object is not fully constructed yet!

  • JavaBean objects are mutable, so this approach is not compatible with immutable pattern.

  • JavaBean require an extra effort in handling thread-safety.

The JavaBeans pattern has serious disadvantages - Joshua Bloch, Effective Java

Static factory methods:

Static factory methods provide another flexibile way to create objects. Instead of using the constructor directly to create an instance, you can define a public static factory method which simply returns an instance of the class!

After all, constructors don’t have the ability to be named!

To make it easier to understand, compare this approach:

    
        LocalDateTime mydate = new LocalDateTime(Instant.now());
    

To this one:

    
        LocalDateTime date = LocalDateTime.ofInstant(Instant.now());
        // Or You can do this : 
        LocalDateTime mydate = LocalDatetime.randomTimeBeforeInstant(Instant.now());
    

Much cleaner and clearer right ?

As we have already seen in a previous chapter, you can create multiple constructors to tackle Java optional parameters! However, it is hard to distinguish between one from another since they all have the same name.

The idea behind this approach is to create static methods with different names so that the intention of object creation become obvious and clear.

You can mark your constructors private, so they can be called only from inside the class and mark the factory methods as static so that they can be invoked without instantiating the class.

    
        public class Person  {
            private String name;
            private int age;
            private Person (String name) {
                this.name = name;
            }
            private Person (String name, int age) {
                this.name = name;
                this.age  = age;
            }
            public void setName(String name) {
                this.name = name;
            }
            public String getName() {
                return this.name;
            }
            public void setAge(int age) {
                this.age = age;
            }
            public int getAge() {
                return this.age;
            }
            public static Person getPersonByName(String name) {
                return new Person(name);
            }
            public static Person getPersonByNameAndAge(String name,int age) {
                return new Person(name,age);
            }
            
            public static void  main(String[] args) {
                Person prs01 = Person.getPersonByName("Samantha");
                Person prs02 = Person.getPersonByNameAndAge("Linda",23);
            }
        }
    

Below are some of the advantages of using static factory methods:

  • Each method can have a different name, unlike constructors, so this helps to clarify the way used to create an instance.

  • Static methods are not required to return new instance every single time!

One of the main downside of this strategy is that it does not scale well, it gets hard to implement and maintain if the number of optional parameters is big.

Builder pattern:

This design pattern provides a very interesting way to create a complex object. The main purpose, is to separate the construction of an object from its representation!

Builder pattern is yet another approach to handle Java optional parameters! It is a good alternative to consider in order to construct objects with optional parameters!

Let’s start by creating a concrete class Employee with some optional properties!

    
        public class Employee {
            // Required properties
            private String firstname;
            private String lastname;
            // Optional properties
            private String email; 
            private String address; 
            private String phone; 
            public Employee() {
            }
            // Getters + Setters
        }
    

You can simply rely on constructor overloading concept and create multiple constructors in your class to cover all the possible parameter combinations!

But what if your class has too many fields? You certainly end up with a quite overwhelming list of ugly and confusing constructors! Right ?

How to avoid this issue? Simply, use builder pattern!

Well, Following are some key points I have considered when implementing builder pattern:

  • I created a public static nested class inside Employee class: EmployeeBuilder! It is our builder class!
  • I defined all the fields of the outer class in EmployeeBuilder.
  • I created a public constructor for EmployeeBuilder with all the required properties as arguments!
  • I created methods in EmployeeBuilder class for setting optional parameters! They return the same Builder object!

Take a look now at our Employee class from the previous example after using builder pattern:

    
        public class Employee {
            // Required properties
            // Optional properties
            private Employee() {
            }
            // Getters + Setters
            // Our builder class
            public static class EmployeeBuilder { 
                private String firstname;
                private String lastname;
                private String email;
                private String address;
                private String phone;
                public EmployeeBuilder(String firstname,String lastname){ 
                    this.firstname = firstname;
                    this.lastname  = lastname;
                }
                public EmployeeBuilder withEmail(String email) {
                    this.email = email;
                    return this;
                }
                public EmployeeBuilder withAddress(String address) {
                    this.address = address;
                    return this;
                }
                public EmployeeBuilder withPhone(String phone) {
                    this.phone = phone;
                    return this;
                }
                public Employee build() {
                    Employee emp  = new Employee();
                    emp.firstname = this.firstname;
                    emp.lastname  = this.lastname;
                    emp.email     = this.email;
                    emp.address   = this.address;
                    emp.phone     = this.phone;
                    return emp;
                }
            }
            public static void main(String[] args) { 
                // First Employee object
                Employee emp01 = new  Employee.EmployeeBuilder("Johnny","Smith")
                                            .withEmail("johnnysmith@email.com")
                                            .withAddress("Johnny Smith ADR")
                                            .withPhone("1 (844) 213-9662")
                                            .build();
                // Second Employee object with some default values
                Employee emp02 = new  Employee.EmployeeBuilder("Samantha","Williams")
                                            .withPhone("1 (833) 737-9738")
                                            .build();
            }
        }
    

Builder pattern improves code flexibility and readability! It allows to create complex objects with optional arguments using step by step approach!

The number of lines increases with the number of parameters. The main downside of builder pattern is verbosity as it requires code duplication!

Nulls and @Nullable annotation:

Java allows to pass null as an argument to set default values for optional parameters! Well, specifying null to indicate that a given argument is optional might seem like the easiest option ever!

You can also use @Nullable annotation, it makes it clear and simple that a method can accept null arguments!

The basic idea of this strategy is simply to null check optional parameters inside the method body to avoid NullPointerException!

    
        public class NullableParams {
            public NullableParams() {
            }
            public void display(String x, String y, int z) { 
                String xx = x != null ? x : "Default X";
                String yy = y != null ? y : "Default Y";
                System.out.println("Printing some random values : "+xx+" "+yy +" "+z); 
            }
            public static void  main(String[] args) { 
                // TODO Auto-generated method stub
                NullableParams obj = new  NullableParams();
                // Java default parameters example
                obj.display(null,null,5);
                obj.display("Hello",null,5);
            }
        }
    
 Printing some random values : Default X Default Y 5
 Printing some random values : Hello Default Y 5

When the method is invoked you can submit nulls for optional arguments! Simple right? No! The devil is in details!

What’s wrong with this approach? Actually, this approach is an anti-pattern! It is considered a bad practice, because it makes the method complicated and hard to read!

Also, we need to be careful when passing null values to an overloaded method! The compiler may get confused if it encounters different declarations - accepting null value - of the same method! This may flag ambiguity error!

    
        public class NullableMathods {
            public NullableMathods() {
            }
            public void print(String str) { 
            }
            public void print(Integer intgr) { 
            }
            public static void  main(String[] args) { 
                NullableMathods myobj = new  NullableMathods();
                myobj.print(null);  // error: reference to fun is ambiguous
                // Both Integer and String are objects in Java which means they can be null.
            }
        }
    

Java varargs:

Varargs as a name, refers to variable arguments, as a concept it provides a simple way to pass variable number of arguments! Perfect to simulate Java optional parameters, right?

Why? Well, sometimes we don’t know how many parameters to supply for a given method!

For what purpose? Mainly to simplify the idea of passing an array as an argument to a method!

How to use it? We can use three dots (…) just after the data type!

Important key points to consider when working with varargs in Java:

  • Only one varargs expression is allowed in the method declaration.
  • If the method has other parameters, varargs must be the last argument.
    
        public class VarargsParams {
            public VarargsParams() {
            }
            public void print(String... values) { 
                System.out.println("Number of parameters: " + values.length);
                for (String value: values) 
                    System.out.println(value);
            }
            public static void  main(String[] args) {
                // TODO Auto-generated method stub
                VarargsParams obj = new  VarargsParams();
                obj.print();
                obj.print("Hello","varargs","In","Java");
            }
        }
    

So If you come across a situation where you don’t know how many parameters to pass to a method, then varargs can be your best friend!

The main drawback of this approach is that, it’s not always safe to use it straightforward, it can lead to heap pollution and ClassCastException!

Another serious thing! How to tell people using your method that it only expect three or four values and not more?

Java 8 optional parameters:

Optional is a new concept introduced in Java 8 as a new type Optional to wrap optional values!

The main purpose of Optional class is to avoid the unwanted ugly NullPointerExceptions and help developing clean, neat and comprehensible code!

Following are some key points about Java 8 Optional class:

  • May or may not contains a non-null value.
  • Null checks are not required.
  • Avoid null pointer exceptions.
  • Reduce Boiler plate code.

Java 8 optional parameters

    
        public class OptionalClass {
            public OptionalClass() {
            }
            public void test(String x, Optional yOpt) { 
                String y = yOpt.isPresent() ? yOpt.get() : "Java 8 default parameter"; 
                System.out.println(x+" "+y); 
            }
            public static void  main(String[] args) {
                // TODO Auto-generated method stub
                OptionalClass opobj =  new  OptionalClass();
                opobj.test("Hello",Optional.of("Required argument"));
                opobj.test("Hi",Optional.empty());
            }
        }
    

Running the above example will give:

 Hello Required argument
 Hi Java 8 default parameter

You can use Java 8 Optional class to denote that a method parameter can be optional, but it is not designed for this purpose!

Well, Optional is primarily intended to represent a method return type! Passing Optional as an argument to a method causes unnecessary wrapping at caller level.

Maps

You can pass a map as a method argument when you have too many parameters and for most of them default values are usually used! It is a good alternative of varargs option!

    
        public class ParamsWithMap {
            public ParamsWithMap() {
            }
            public void foo(Map<String,Object> params) {
                String  param01 = "default"; 
                Integer param02 = 0;
                if (params.containsKey("param01")) { 
                    if (params.get("param01") instanceof String) {  
                        param01 = (String)params.get("param01"); 
                    } 
                } 
                if (params.containsKey("param02")) { 
                    if (params.get("param02") instanceof Integer) { 
                        param02 = (Integer)params.get("param02"); 
                    } 
                } 
                System.out.println(param01 +" : "+param02);
            }
            public static void  main(String[] args) {
                ParamsWithMap mapobj = new  ParamsWithMap();
                Map<String,Object> parameters = new HashMap<>();
                parameters.put("param01", "Hello Test");
                parameters.put("param02", 5);
                mapobj.foo(parameters);
                
                Map<String,Object> opparameters = new HashMap<>();
                opparameters.put("param01", "Azhwani");
                mapobj.foo(opparameters);
            }
        }
    

The above example will yield on the following output:

 Hello Test : 5
 Azhwani : 0

Java 9 introduced new factory methods to simplify the logic of creating immutable Maps!

    
        public class ParamsWithJava9Map {
            public ParamsWithJava9Map() {
            }
            public void boo(Map<String,Object> params) {
                String  x = "default"; 
                Integer y = 0;
                if (params.containsKey("x")) { 
                    if (params.get("x") instanceof String) {  
                        x = (String)params.get("x"); 
                    } 
                } 
                if (params.containsKey("y")) { 
                    if (params.get("y") instanceof Integer) { 
                        y = (Integer)params.get("y"); 
                    } 
                } 
                System.out.println(x +" : "+y);
            }
            public static void  main(String[] args) {
                ParamsWithJava9Map mapobj = new  ParamsWithJava9Map();
                Map<String,Object> map1 = Map.of("x", "Hello java 9", "y", 62);
                mapobj.boo(map1);
                
                Map<String,Object> map2 = Map.of("x", "Another hello");
                mapobj.boo(map2);
            }
        }
    

No one will complain even the compiler if you want use a map to hold default parameters but it is not designed for this purpose at all!

Using map to handle optional parameters in Java can cause conditional logic inside the method which can lead to low performance at some point!

Conclusion:

That’s all, let’s sum up all what we learned in this article: Java does not support default parameters concept to avoid complexity and ambiquity!

If you come across a situation where you have to implement optional arguments in Java, then you can use one of the methods I mentioned above!

if you have any questions about Java optional parameters, feel free to discuss them with us in the comment section.

Happy learning and have a nice day!