Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Optional Parameters Handling Strategy in Java

DZone's Guide to

Optional Parameters Handling Strategy in Java

A detailed comparison of six different ways of handling optional parameters in Java.

· Java Zone
Free Resource

The single app analytics solutions to take your web and mobile apps to the next level.  Try today!  Brought to you in partnership with CA Technologies

Often we face a situation where we need to design an object that expects a lot of parameters from the client. Some parameters are required and some are optional. In this article, we will focus on various strategies by which we can design such objects, as well as their pros and cons.

Strategy 1. Telescopic Constructors

To design such objects, we can use a chain of overloading constructors. First, a minimal constructor is taking only the required parameters, then delegate calls another constructor, which takes an optional parameter.

Then this constructor calls another one, which takes another optional parameter until all optional parameters are covered. When calling another constructor, it can pass the default value for an optional parameter.

Pros

  • For a small number of parameters, this is a good way to design objects.
  • Very easy to implement.

Cons

  • For a large number of parameters, it creates problems. The developer often confuses passing parameters.
  • If two adjacent parameters have the same datatype, and a developer can unintentionally swap values. The compiler won't complain, but it creates a genuine problem at runtime and is very hard to track.

Example

package com.example.builder;

public class EmployeeTelescopic {

       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress ;    

       public EmployeeTelescopic(String name,Integer empId,String company)
       {
              this(name,empId,company,0);
       }

       public EmployeeTelescopic(String name,Integer empId,String company,int passport)
       {
              this(name,empId,company,passport,"NA");
       }

       public EmployeeTelescopic(String name,Integer empId,String company,Integer passport,String tempAddress)
       {
           this.name=name;
           this.empId=empId;
           this.company=company;
           this.passport=passport;
           this.tempAddress=tempAddress;                        
       }

       @Override
       public String toString() {
              return "EmployeeTelescopic [name=" + name + ", empId=" + empId
                           + ", company=" + company + ", passport=" + passport
                           + ", tempAddress=" + tempAddress + "]";
       }
       public static void main(String[] args) {            
              EmployeeTelescopic emp = new EmployeeTelescopic("Shamik",100,"IBM");
              EmployeeTelescopic emp1 = new EmployeeTelescopic("Akash",101,"IBM",1234,"1,bangalore");
              System.out.println(emp);
              System.out.println(emp1);
       }
}


Strategy 2By Getters and Setters

Create a class and expose every property through getters and setters. The client will set the property using a setter, and access it via getters.

Pros

  • Easy to implement.
  • In setters, you can set validation or pass a default value for optional parameters.

Cons

  • If an object is shared between multiple threads, it is possible that one thread will just create the object and try to set properties, while another thread accesses the object. As its properties have not been set, an exception can occur.
  • Properties are exposed through getters and setters, and extra care is needed to make this object immutable.

Strategy 3: Using Varargs

Create a constructor like  Employee(Object… args). The client can pass variable length parameters. A constructor then checks the type of each parameter by using an instanceof operator.

If a type is same as a bean property, set a value. Otherwise, throw an IllegalArgument Exception.

Pros

  • None

Cons

  • For each property, you need to check the type so it loses static type checking.

  • There is a conditional block for each property, which increases cyclomatic complexity.

Strategy 4: Using Map

Same as varargs, but instead of varargs we use a map.

Pros

  • None

Cons

  • For each property, you need to check type so it loses static type checking.

  • There is a conditional block for each property, which increases cyclomatic complexity.

Example

package com.example.builder;

import java.util.HashMap;
import java.util.Map;

public class EmployeeMap {
       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress ;
      public  EmployeeMap(Map<String,Object> map) throws IllegalArgumentException
       {
              if(!(map.containsKey("name") && map.containsKey("empId") && map.containsKey("company")))
              {
                     thrownew IllegalArgumentException("Required  Parameter missing");
              }
              if((map.get("name")==null || map.get("empId")==null ||  map.get("company")==null))
              {
                     thrownew IllegalArgumentException("Required  Parameter missing");
              }             
              if(map.get("name") instanceof String)
              {
                     this.name =(String) map.get("name") ;
              }
              else{
                     thrownew IllegalArgumentException("name Parameter  type is wrong");
              }             
              if(map.get("empId") instanceof Integer)
              {
                     this.empId =(Integer) map.get("empId") ;
              }
              else{
                     thrownew IllegalArgumentException("enpId Parameter  type is wrong");
              }
              if(map.get("company") instanceof String)
              {
                     this.company =(String) map.get("company") ;
              }
              else{
                     thrownew IllegalArgumentException("company Parameter  type is wrong");
              }                          
              if(map.containsKey("passport") && (map.get("passport") instanceof Integer))
              {
                     this.passport = (Integer)map.get("passport");
              }
              else
              {
                     this.passport =0;
              }            
              if(map.containsKey("tempAddress") && (map.get("tempAddress") instanceof String))
              {
                     this.tempAddress = (String)map.get("tempAddress");
              }
              else
              {
                     this.tempAddress="NA";
              }
       }     

       @Override
       public String toString() {
              return "EmployeeMap [name=" + name + ", empId=" + empId + ", company="
                           + company + ", passport=" + passport + ", tempAddress="
                           + tempAddress + "]";
       }

       public static void main(String[] args) {             
              try
              {
              Map map = new HashMap<String,Object>();
              map.put("name", "Shamik");
              map.put("empId", 100);
              map.put("company", "IBM");
              EmployeeMap emp = new EmployeeMap(map);

              Map map1 = new HashMap<String,Object>();
              map1.put("name", "Akash");
              map1.put("empId", 101);
              map1.put("company", "IBM");
              map1.put("passport", "1234");
              map1.put("tempAddress", "1,bangalore");
              EmployeeMap emp1 = new EmployeeMap(map1);

              System.out.println(emp);
              System.out.println(emp1);

              }
              catch(IllegalArgumentException ex)
              {
                     ex.printStackTrace();
              }
       }
}


Strategy 5: Null values

Here, the client pass optional value as null, and the constructor checks if value is null, then sets a default value for optional parameters. If a parameter is required and null, the constructor throws an exception.

Pros

  • For a small number of parameters this is good way to design objects.
  • Very easy to implement.

Cons

  • For large number of parameters, it creates problems. Developer has to pass null value for optional parameters.
  • If two adjacent parameters have the same datatype and developer can unintentionally swap values. Compilers do not complain, but it creates a genuine problem at runtime, and it is very hard to track.

Example

package com.example.builder;
public class EmployeeNull {

       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress;


       public EmployeeNull(String name,Integer empId,String company,Integer passport,String tempAddress) throws IllegalArgumentException
       {
              if(name ==null || empId==null || company==null)
              {
                     thrownew IllegalArgumentException("Required  Parameter missing");
              }

              this.name=name;
             this.empId=empId;
            this.company=company;
              this.passport= passport != null?passport:0;
              this.tempAddress = tempAddress !=null? tempAddress:"NA";
       }
       @Override
       public String toString() {
              return "EmployeeNull [name=" + name + ", empId=" + empId + ", company="
                           + company + ", passport=" + passport + ", tempAddress="
                           + tempAddress + "]";
       }


       public static void main(String[] args) throws IllegalAccessException {

              EmployeeNull emp = new EmployeeNull("Shamik",100,"IBM",null,null);
              EmployeeNull emp1 = new EmployeeNull("Akash",101,"IBM",1234,"1,blore");
              System.out.println(emp);
              System.out.println(emp1);

       }


}


Strategy 6: Builder Pattern

Use a nested static class, which acts as a builder of this bean. The Builder takes required parameters as its constructor arguments, and for each optional parameter there will be a helper method which sets the value and returns a Builder instance itself. So we can invoke another helper method for a parameter. The builder pattern maintains a fluent interface pattern (Channeling of methods).  At last, build() method returns an Immutable bean object.

Pros

  • Immutability is achieved easily, so Objects are thread-safe by design.
  • To set optional parameters, the parameter position is not necessary, as each parameter has a helper method client can invoke them. It offers fluidity by a chain of methods pattern.

Cons

  • Complex to implement.

Example

package com.example.builder;
public class Employee {

       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress ;

       private Employee()
       {

       }
       privatestaticclass EmployeeBuilder
       {
              private String name;
              private Integer empId;
              private String company;
              private Integer passport;
              private String tempAddress ;
              public EmployeeBuilder(String name,Integer empId,String company)
              {
                    this.name=name;
                    this.empId=empId;
                    this.company=company  ;                
              }

              public EmployeeBuilder setPassport(Integer passport)
              {
                    this.passport=passport;
                     return this;

              }

              public EmployeeBuilder setTempAddress(String address)
              {
                     this.tempAddress=address;
                     return this;

              }

              public Employee build()
              {
                     Employee emp = new Employee();

                     emp.name=this.name;
                     emp.empId=this.empId;
                     emp.company=this.company;
                     emp.passport=this.passport!=null?this.passport:0;
                     emp.tempAddress=this.tempAddress!=null?this.tempAddress:"NA";
                     return emp;
              }





       }



       @Override
       public String toString() {
              return "Employee [name=" + name + ", empId=" + empId + ", company="
                           + company + ", passport=" + passport + ", tempAddress="
                           + tempAddress + "]";
       }
       public static void main(String[] args) {

              Employee emp = new Employee.EmployeeBuilder("Shamik", 100, "IBM").build();
              Employee emp1 = new Employee.EmployeeBuilder("Akash", 101, "IBM").setPassport(1234).setTempAddress("1,bangalore").build();
              System.out.println(emp);
              System.out.println(emp1);
       }

}

Comparison: Please see the picture below.

Image title

Conclusion: For fewer parameters, four or less, use the Telescopic constructor pattern.

For a larger number of parameters use the Builder pattern.

CA App Experience Analytics, a whole new level of visibility. Learn more. Brought to you in partnership with CA Technologies.

Topics:
java ,parameter

Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}