Losing Polymorphism with Java Lambda Expressions

Yesterday, I decided to re-write with lambdas one of the example I use while explaining polymorphism during my Java trainings. I realize that lambdas promote functional style of programming and implementing inheritance via composition over polymorphism, which is one of the crucial object-oriented features. Still, I decided to try mixing traditional OOP and functional styles to see what happens.

The Task

The class Person has two descendants: Employee and Contractor. Also, there is an interface Payable that’s used to increase pay for workers.

public interface Payable {
   int INCREASE_CAP = 20;
   boolean increasePay(int percent);
}

Both Employee and Contract implement Payable, but the implementation of the increasePay() should be different. you can increase pay to the Employee by any percent, but the Contractor’s pay increase should stay under INCREASE_CAP.

The OOP version

Here’s the class Person:

public class Person {
	private String name;
	
	public Person(String name){
		this.name=name;
	}

	public String getName(){
		return "Person's name is " + name; 
	}
}

Here’s the Employee:

public class Employee extends Person  implements Payable{

   public Employee(String name){
     super(name);
    }
    public boolean increasePay(int percent) {
    System.out.println("Increasing salary by " + percent + "%. "+ getName()); 
    return true;
  }
}

Here’s the Contractor:

 public class Contractor extends Person implements Payable {
	
   public Contractor(String name){
 	super(name);
    }
   public boolean increasePay(int percent) {
	if(percent < Payable.INCREASE_CAP){
	  System.out.println("Increasing hourly rate by " + percent + 
                                      "%. "+ getName()); 
	  return true;
	} else {
	   System.out.println("Sorry, can't increase hourly rate by more than " + 
                       Payable.INCREASE_CAP + "%. "+ getName());
	   return false;
	}
  }
}

Here’s the code that will perform pay increase in a polymorphic way:

public class TestPayInceasePoly {

  public static void main(String[] args) {

     Payable workers[] = new Payable[3];
	workers[0] = new Employee("John");
	workers[1] = new Contractor("Mary");
	workers[2] = new Employee("Steve");		
	
        for (Payable p: workers){
	    p.increasePay(30);
	 }
  }
}

The output of this program looks like this:

Increasing salary by 30%. Person’s name is John
Sorry, can’t increase hourly rate by more than 20%. Person’s name is Mary
Increasing salary by 30%. Person’s name is Steve

Introducing Lambdas

Now I decided to experiment with lambdas. Namely, I wanted to pass a function as an argument to a method. I wanted to extract the increase pay logic from Employee and Contractor into lambda expressions and pass them to workers as argument to their methods.

I left the code of the Payable interface without any changes.

Here’s the new Person class that includes the method validatePayIncrease, which will take a lambda expression as a first argument (passing a function to a method):

public class Person {
	
	private String name;

	public Person (String name){
		this.name = name;
	}
	
	public String getName(){
		return name;
	}
	
	public boolean validatePayIncrease(Payable increaseFunction, int percent) {
			 
         boolean isIncreaseValid= increaseFunction.increasePay(percent); 
         	 
         System.out.println( " Increasing pay for " + name + " is " + 
        	              (isIncreaseValid? "valid.": "not valid."));
		 return isIncreaseValid;
	}
}

The new version of Employee doesn’t implements Payable:

public class Employee extends Person{

  // some other code specific to Employee goes here

  public Employee(String name){
	  super(name);
  } 
}

The new version of Contractor doesn’t implements Payable either:

	  
public class Contractor extends Person{
    

  // some other code specific to Contractor goes here

    public Contractor(String name){
       super(name);
    }
}

Finally, the program that will increase the pay to all workers passing different lambda expressions to employees and contractors.

public class TestPayIncreaseLambda {
	
  public static void main(String[] args) {

        Person workers[] = new Person[3];
		workers[0] = new Employee("John");
		workers[1] = new Contractor("Mary");
		workers[2] = new Employee("Steve");		

	  // Lambda expression for increasing Employee's pay
	   Payable increaseRulesEmployee = (int percent) -> {
				return true;
	   };
	   
		// Lambda expression for increasing Contractor's pay	   
	    Payable increaseRulesContractor = (int percent) -> {
	    	if(percent > Payable.INCREASE_CAP){
	    		System.out.print(" Sorry, can't increase hourly rate by more than " + 
	    	             Payable.INCREASE_CAP + "%. "); 
				return false;
			} else {	
				return true;
			}
	   };	
	   
	   for (Person p: workers){
		   if (p instanceof Employee){
			   // Validate 30% increase for every worker
			   p.validatePayIncrease(increaseRulesEmployee, 30); 
		   } else if (p instanceof Contractor){
			   p.validatePayIncrease(increaseRulesContractor, 30);
		   }
	   }
  }

}

As you see, I’m passing one or the other lambda expression to the method validatePayIncrease. Running this program produces the following output:

Increasing pay for John is valid.
Sorry, can’t increase hourly rate by more than 20%.  Increasing pay for Mary is not valid.
Increasing pay for Steve is valid.

It works, but so far I like my OOP version better than the lambda’s one:

1. In the OOP version I’ve enforced a contract – both Employee and Contractor must implement Payable. In the lambda’s version it’s gone on the class level. On the plus side, the strong typing still exists in the lambda’s version –  the type of the valiastePayIncrease first argument is Payable.

2. In the OOP version I didn’t use type checking, but in the lambda’s version this ugly instanceof creeped in.

While it’s cool that I can pass the function to the object and execute it there, the price seems to be high. Let’s see if this code can be further improved.

Getting rid of the class hierarchy

Currently the code in Employee and Contractor is the same. If the only difference in their code is implementation of the validatePayIncrease method, we can remove the inheritance hierarchy and just add the property boolean workerStatus to the class Person to distinguish employees from contractors.

Let’s get rid of the classes Employee and Contractor and modify the Person. I’ll add the second argument to the constructor workerStatus.

public class Person {
	
	private String name;
	private char workerStatus;  // 'E' or 'C'

	public Person (String name, char workerStatus){
		this.name = name;
		this.workerStatus=workerStatus;
	}
	
	public String getName(){
		return name;
	}
	
	public char getWorkerStatus(){
		return workerStatus;
	}
	
	public boolean validatePayIncrease(Payable increaseFunction, int percent) {
			 
         boolean isIncreaseValid= increaseFunction.increasePay(percent); 
         	 
         System.out.println( " Increasing pay for " + name + " is " + 
        	              (isIncreaseValid? "valid.": "not valid."));
		 return isIncreaseValid;
	}
}

The code of the TestPayIncreaseLambda will become simpler now. We don’t need to store objects of different types in the array workers and can get rid of the instanceof:

public class TestPayIncreaseLambda {
	
  public static void main(String[] args) {

        Person workers[] = new Person[3];
		workers[0] = new Person("John", 'E');
		workers[1] = new Person("Mary", 'C');
		workers[2] = new Person("Steve", 'E');		

	  // Lambda expression for increasing Employee's pay
	   Payable increaseRulesEmployee = (int percent) -> {
				return true;
	   };
	   
		// Lambda expression for increasing Contractor's pay	   
	    Payable increaseRulesContractor = (int percent) -> {
	    	if(percent > Payable.INCREASE_CAP){
	    		System.out.print(" Sorry, can't increase hourly rate by more than " + 
	    	             Payable.INCREASE_CAP + "%. "); 
				return false;
			} else {	
				return true;
			}
	   };	
	   
	   for (Person p: workers){
		   if ('E'==p.getWorkerStatus()){
			   // Validate 30% increase for every worker
			   p.validatePayIncrease(increaseRulesEmployee, 30); 
		   } else if ('C'==p.getWorkerStatus()){
			   p.validatePayIncrease(increaseRulesContractor, 30);
		   }
	   }
  }
}

If a new type of a worker will be introduced (e.g. foreign workers), we’ll just need to add another lambda expression to the class TestPayIncreaseLambda that implements business rules for foreign workers.

The purists may not like the fact that I’m using hardcoded ‘E’ and ‘C’.You can add a couple of final variables EMPLOYEE and CONTRACTOR at the top of this class.

So what’s the verdict? Losing polymorphism may not be a bad thing. The code became simpler, we’ve removed two classes, but didn’t lose the type enforcement (the Payable interface). Software developers that use functional programming languages live without polymorphism and don’t miss it.

P.S. As a matter of fact you can lose the Payable interface too. This would require a different implementation of our lambda expressions with the help of the interfaces from the new package java.util.function. But this should be a topic of a separate blog. OK, here’s a hint: I did it using the BiFunction interface.

11 thoughts on “Losing Polymorphism with Java Lambda Expressions

  1. Hello Yakov,

    It seems that in your example the main idea of using lambda is to extract the differences in behaviour between Employee and Contractor to lambda. After extracting, Employee and Contractor has absolutely the same code and moreover they are the same as Person. So the question is why do we need them at all?

    The better solution would be to delete those classes and leave only Person. Since we have to adjust Person to our needs (provide different functions of increasePayment to different instances of Person), it would be better to pass right function to the Person constructor.

    So basically the code would be:

    public class Person {
    private String name;
    private Payable increaseRules;

    public Person (String name, Payable increaseRules) {
    this.name = name;
    this.increaseRules = increaseRules;
    }

    public String getName(){
    return name;
    }

    public boolean validatePayIncrease(int percent) {

    boolean isIncreaseValid = increaseRules.increasePay(percent);

    System.out.println( ” Increasing pay for ” + name + ” is ” +
    (isIncreaseValid? “valid.”: “not valid.”));

    return isIncreaseValid;
    }
    }

    The program:
    // Lambda expression for increasing Employee’s pay
    Payable increaseRulesEmployee = (int percent) -> {
    return true;
    };

    // Lambda expression for increasing Contractor’s pay
    Payable increaseRulesContractor = (int percent) -> {
    if(percent > Payable.INCREASE_CAP){
    System.out.print(” Sorry, can’t increase hourly rate by more than ” +
    Payable.INCREASE_CAP + “%. “);
    return false;
    } else {
    return true;
    }
    };

    Person workers[] = {new Person(“John”, increaseRulesEmployee),
    new Person(“Mary”, increaseRulesContractor),
    new Person(“Steve”, increaseRulesEmployee)};

    for (Person p: workers){
    p.validatePayIncrease(30);
    }

    1. This is right, we can remove the inheritance hierarchy in this example. The only issue is that you won’t know who’s Employee and who’s contractor. Adding workerType property to the Person class would be required in this case.

  2. How about using a functional interface as a member of class Person? We still have to use type checking, but the contract that both Employee and Contractor must implement Payable is enforced:

    public class TestingLambdas {

    public static void main(String[] args) {

    Person workers[] = new Person[3];
    workers[0] = new Employee(“John”);
    workers[1] = new Contractor(“Mary”);
    workers[2] = new Employee(“Steve”);

    for (Person p: workers){

    if (p instanceof Employee) {
    ((Employee)p).increasePay.increasePay(30);
    }
    else if (p instanceof Contractor){
    ((Contractor)p).increasePay.increasePay(30);
    }
    }

    }

    }

    //functional interface
    interface Payable {
    int INCREASE_CAP = 20;
    boolean increasePay(int percent);
    }

    class Employee extends Person{

    public Employee(String name){
    super(name);

    increasePay = (int percent) -> {
    System.out.println(“Increasing salary by ” + percent + “%. ” + getName());
    return true;
    };
    }
    }

    class Contractor extends Person{

    public Contractor(String name){
    super(name);

    increasePay = (int percent) -> {
    if(percent > Payable.INCREASE_CAP){
    System.out.println(” Sorry, can’t increase hourly rate by more than ” +
    Payable.INCREASE_CAP + “%.” + getName());
    return false;
    } else {
    System.out.println(“Increasing hourly rate by ” + percent +
    “%.” + getName());
    return true;
    }
    };
    }
    }

    class Person {
    private String name;
    Payable increasePay;

    public Person(String name){
    this.name=name;
    }

    public String getName(){
    return “Person’s name is ” + name;
    }
    }

    1. Members of a class (unless they are abstract) don’t enforce subclasses to do anything. The final version of the app doesn’t even have inheritance.

      1. I see what you say about enforcing contracts in subclasses, but I don’t understand the statement about inheritance. If I don’t define increasePay in one of the subclasses of Person, it would certainly be inherited from its superclass, wouldn’t it? As in the following program:

        public class TestingLambdas {

        public static void main(String[] args) {

        Person workers[] = new Person[4];
        workers[0] = new Employee(“John”);
        workers[1] = new Contractor(“Mary”);
        workers[2] = new Employee(“Steve”);
        workers[3] = new Person(“Cristina”);

        for (Person p: workers){

        p.increasePay.increasePay(30);
        }

        }

        }

        //functional interface
        interface Payable {
        int INCREASE_CAP = 20;
        boolean increasePay(int percent);
        }

        class Employee extends Person{

        public Employee(String name){
        super(name);
        increasePay = (int percent) -> {
        System.out.println(“Increasing salary by ” + percent + “%. ” + getName());
        return true;
        };
        }

        }

        class Contractor extends Person{

        public Contractor(String name){
        super(name);

        increasePay = (int percent) -> {
        if(percent > Payable.INCREASE_CAP){
        System.out.println(” Sorry, can’t increase hourly rate by more than ” +
        Payable.INCREASE_CAP + “%. ” + getName());
        return false;
        } else {
        System.out.println(“Increasing hourly rate by ” + percent +
        “%.” + getName());
        return true;
        }
        };
        }
        }

        class Person {
        private String name;
        Payable increasePay = (int percent) -> {
        System.out.println(“This person cannot be paid. ” + getName());
        return false;
        };

        public Person(String name){
        this.name=name;
        }

        public String getName(){
        return “Person’s name is ” + name;
        }
        }

        1. By declaring a dummy increasePay in Person you’re not enforcing anything. You just hope that the programmer who will write subclasses of Person will use the variable increasePay and not create a new one, say addMoney.

  3. “So what’s the verdict? Losing polymorphism may not be a bad thing, or is it?”

    It depends 🙂 If you really need to know the type of Person (Employee or Contractor), I like the very first version with polymorphism more. If you don’t need that information once you constructed the Person, than it’s ok to loose inheritance.

  4. Hello, I think that will you also can pas lambda as a parameter to person costructor like that:
    workers.add(new Person(“Peter”, for_workers));
    workers.add(new Person(“Mary”, for_contractors));
    where lambdas defined like that:

    Payable for_workers = (percent) -> {
    +      return true;
    +    };
    +
    +    Payable for_contractors = (percent) -> {
    +      if (percent > Payable.INCREASE_PAY) {
    +        return false;
    +      } else {
    +        return true;
    +      }
    +    };
    

    and finally function for increasing payment is like:

     public void increasePayment(int percent) {
    +    if (validator_func.increasePay(percent)) {
    +      System.out.println("Payment increased for " + name + " on percent");
    +    } else {
    +      System.out.println("sorry we cannot increase payment for " + name + " on " + percent);
    +    }
    +  }
    
    

    and full code :

    +public class Person {
    +
    +  String name;
    +  Payable validator_func;
    +
    +  public Person(String name, Payable validator_func) {
    +    this.name = name;
    +    this.validator_func = validator_func;
    +  }
    +
    +  public void increasePayment(int percent) {
    +    if (validator_func.increasePay(percent)) {
    +      System.out.println("Payment increased for " + name + " on percent");
    +    } else {
    +      System.out.println("sorry we cannot increase payment for " + name + " on " + percent);
    +    }
    +  }
    +
    +  public static void main(String[] args) {
    +
    +    Payable for_workers = (percent) -> {
    +      return true;
    +    };
    +
    +    Payable for_contractors = (percent) -> {
    +      if (percent > Payable.INCREASE_PAY) {
    +        return false;
    +      } else {
    +        return true;
    +      }
    +    };
    +
    +
    +    List workers = new ArrayList();
    +    workers.add(new Person("Peter", for_workers));
    +    workers.add(new Person("Mary", for_contractors));
    +
    +    for (Person worker : workers) {
    +      worker.increasePayment(30);
    +    }
    +
    +  }
    +
    +
    Add a comment to this line
    +}
    

Leave a comment