5 Curious cases of Overloading & Generics

5 Curious cases of Overloading & Generics


If you are preparing for an Interview you can expect questions on  generics in java interview for sure , whether it is an position for Team lead or experience developer or Junior. But Generics gets more complicated when it mix with overloading in java. Let see the different scenarios when Generic mix with overloading.

What is generics in Java ?

Generics is technique which facilitates to declare a generic type(T, Irrespective of any Datatype in Java) in methods or class, which will be resolved later when caller of the method or class provide the actual Data Type(Integer, Boolean, String or ant Custom class) and ensure compile time safety but at the run-time it removes the type safety, we call this process as Type erasure.

Benefits of Generics:

By generics, we can achieve some level of the generic type declaration. To be specific Java is a static type language which means when we declare properties or methods we have to provide the type of the parameter or return type at the time of declaration.

But by generics we can defer the process we can declare methods or properties using generic syntax and later on caller can decide what data type pass to it, but remember it is not same as dynamic type language like javascript where var declaration means it represents any data types, on other hand Generics introduce the Type Inference from a bounded context, from Java perspective which is the first step towards functional programming(lambda) adaptation.

Challenges :

The most tricky part of generics is  by Type erasure, java removes the bounded type at runtime, so in java runtime generics and non-generics method/property declaration both are same no difference at all, to maintain backward compatibility  but generics ensure compile time safety so if you declare generics with certain type it only bounds to that certain type if you want to pass any other type compiler will complain instantly.

But as run time its type vanishes so in the case of Overloading we have to think what the method looks after type erasure erases the generic type, to do a perfect overloading unless compiler will complain.

In this Article, we will see 5 such scenarios which can occur often if I miss any please write in the comment section so I can add them to the Article.


Hope you are familiar with the generic syntax and how to use it.




Scenario 1 :

public Integer add(Integer a, Integer b) {
        System.out.println(a+b);
        return a+b;
    }

    /* (non-Javadoc)
    * @see com.example.generics.ICalculate#add(java.lang.Object, java.lang.Object)
    */

    public <T> T add(T a, T b) {
       
        Integer a1 = (Integer)a;
        Integer b1 = (Integer)b;
        Integer resut = a1+b1;
        System.out.println(" Result is" + resut);
        return (T) resut;
       
       
    }


See the two versions of add method can you tell me is it a valid overloading?

Yes this is a valid over loading as after type erasing public <T> T add(T a, T b) this method signature converts to , public Object  add(Object a, Object b) clearly it is different than
public Integer add(Integer a, Integer b) .



Scenario 2:

Now see the following version of overloading


public <T> T add(T a, T b) {
       
        Integer a1 = (Integer)a;
        Integer b1 = (Integer)b;
        Integer resut = a1+b1;
        System.out.println(" Result is" + resut);
        return (T) resut;
       
       
    }
   
   
   
public  Object add(Object a,Object b) {
       
        Integer a1 = (Integer)a;
        Integer b1 = (Integer)b;
        Integer resut = a1+b1;
        System.out.println(" Result is" + resut);
        return  resut;
       
       
    }



Can you tell me is it a valid overloading?

Probably you guess the answer it is not as I told you in previous example public <T> T add(T a, T b) this method signature converts to , public Object  add(Object a, Object b) after type erasing so now both signatures looks same so compiler will complain.


Scenario 3:

Now take the following version

public <T> T add(T a, T b) {
       
        Integer a1 = (Integer)a;
        Integer b1 = (Integer)b;
        Integer resut = a1+b1;
        System.out.println(" Result is" + resut);
        return (T) resut;
       
       
    }

public <T extends Number> T add(T a, T b) {
       
        Integer a1 = (Integer)a;
        Integer b1 = (Integer)b;
        Integer resut = a1+b1;
        System.out.println(" Result is" + resut);
        return (T) resut;
       
       
    }


Is the above a valid Overloading?

Yes it is a valid overloading as after type erasing  public <T> T add(T a, T b)  changed to public Object add(Object a,Object b) but public <T extends Number> T add(T a, T b)  changed to
public Number add(Number a, Number b) as T extends Number means any type which extends Number so after Type erasing it will take Number as infer type.


Exercise :

Can you tell me Is it valid overloading with explanation -- without paste that code in editor?
public Integer add(Integer a, Integer b) {
        System.out.println(a+b);
        return a+b;
    }


public <T extends Number> T add(T a, T b) {
       
    System.out.println(a.getClass().getName());
        Integer a1 = (Integer)a;
        Integer b1 = (Integer)b;
        Integer resut = a1+b1;
        System.out.println(" Result is" + resut);
        return (T) resut;
       
       
    }



Now let take a slightly different example where I try to pass a data type to a collection which will be the bounded context for that collection.

Scenario 4:

public void add(List<Integer> list) {
   
    System.out.println("add list of Integers");
   
   
}

public void add(List<?> list) {
   
    System.out.println("add/concat  list of any type");
   
   
}


Is it a valid overloading?

The Answer is, unfortunately, no, because after type erasing generics will lose its type and would become same as our old list.

So both signatures public void add(List<Integer> list)  and public void add(List<?> list) change to  public void add(List list)  and public void add(List list) so compiler will complain for sure.


Scenario 5 :


public void add(List<Integer> list) {
   
    System.out.println("add list of Integers");
   
   
}

public void add(List<Double> list) {
   
    System.out.println("add list of Integers");
   
   
}


As previous reason it is also not a perfect overloading.

So always pay attention of your generics signature while extending a class or overload method.













Toss out Inheritance Forest

Toss out Inheritance Forest

In the previous article, we saw How we can solve the Inheritance forest problem by Decorator pattern,
In case if you missed that Article you can go here::

For who does not like to click the link while reading an Article, I just quickly state the Problem statement.

Problem Statement:
We have to create a ball and same can be made by any materials and can have any color.
So here, two constraints are

A ball can be made by any material (Rubber, plastic, Polythene, Feather, Leather, Leather-Rubber etc.).
Can be any color (Red, Orange, Blue, Yellow, Blue-Yellow etc).
Now, the ball can be any combination of material and color.(Red Rubber Ball, Yellow Rubber Ball etc).

Choosing Design Pattern :

To see the problem one thing is sure Ball’s material and Ball’s color are the variable parts here and it can have any possible values and the second part of the problem statement is it can be any combinations of material and color, technically any combination of two variable parts.

So the crux of the problem is if we create a child class which implements IColor, IMaterial interfaces then for each possible combinations we have to create a concrete class as an outcome creates  Inheritance Forest.

Challenges:
So we have to design our problem in such a way where we can supply the variable parts from outside or technically caller of our design can choose the material and color runtime and pass the same into our framework.

In a generic way  most of the use cases follow the same context, In Functional programming, we can do it through Higher order function where we can pass a function as an argument and that function holds the computation logic.

In Java, we can achieve it through IOC Where Higher level component talk to lower level components via an Interface,

Lower level component holds the strategy implementation and we pass them to Higher order components which hold the Algorithm.

we can achieve it through Strategy Pattern.



Strategy pattern :

According to Wikipedia definition , In computer programming, the strategy pattern (also known as the policy pattern) is a behavioural software design pattern that enables an algorithm's behavior to be selected at runtime.
So by using Strategy pattern, we can push Material and Color into ball creation algorithm as behavior/strategy can be pushed at runtime so we can create different combinations also.

Let See the diagram

Expel Inheritance forest by Strategy pattern





Here I create two interfaces called IColor and IMaterial, each has own concrete implementations Now High-Level components talks to lower level through those interfaces and caller of the program choose one of the implementations of interfaces and pass them to Higher level components for the algorithm computation.

Let see How the code look likes

IColor.java

package com.example.strategy;

public interface IColor {

   public String createColor();

}

IMaterial.java

package com.example.strategy;

public interface IMaterial {
   
   public String createMaterial();

}

Now the Higher Order Component

BallManager.java

package com.example.strategy;

public class BallManager {
   
   IColor color;
   IMaterial material;
   
   public void setColor(IColor color) {
      this.color = color;
   }


   public void setMaterial(IMaterial material) {
      this.material = material;
   }



   public void createBall(){
      String colorStr = color.createColor();
      String materialStr = material.createMaterial();
      displayInfo(colorStr,materialStr);
     
   }
   private void displayInfo(String... strings){
      System.out.println("Add Ball Color ::" + strings[0] );
      System.out.println("Add Ball material ::" + strings[1] );
      System.out.println("Produce the Ball");
   }

}


Client of Our framework creates Actual implementation on the fly.

Caller.java

package com.example.strategy;

public class Caller {
   public static void main(String[] args) {
     
      IColor redColor = new IColor(){

          @Override
          public String createColor() {
              return "Red";
          }
         
      };
     
      IColor blueColor = new IColor(){

          @Override
          public String createColor() {
              return "Blue";
          }
         
      };
     
      IMaterial plasticMaterial = new IMaterial(){

          @Override
          public String createMaterial() {
              return "Plastic";
          }
         
      };
     
     
      BallManager mgr = new BallManager();
      mgr.setColor(blueColor);
      mgr.setMaterial(plasticMaterial);
      mgr.createBall();
      System.out.println("=====================");
      mgr.setColor(redColor);
      mgr.setMaterial(plasticMaterial);
      mgr.createBall();
   }

}


Output:

Add Ball Color ::Blue
Add Ball material ::Plastic
Produce the Ball
=====================
Add Ball Color ::Red
Add Ball material ::Plastic
Produce the Ball