1

Could anybody tell me the example below violate LSP or not?

I have an example:

public class Person {
    private String name;
    private Integer age;

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

    public void validate() {
        if (age == null || age < 0) {
            throw new IllegalArgumentException("Age can not be null");
        }
    }
}

and the subclass:

public class Employee extends Person {
    private String employeeCode;

    public Employee(String name, Integer age, String employeeCode) {
        super(name, age);
        this.employeeCode = employeeCode;
    }

    @Override
    public void validate() {
        super.validate();
        if (employeeCode == null) {
            throw new IllegalArgumentException("Employee code can not be null");
        }
    }
}

and the main class:

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Person", 10);
        validate(person); // will be ok. does not throw any exception

        Person employee = new Employee("Employee", 30, null);
        validate(employee); // will be throw IllegalArgumentException because subtype's employee code is null
    }

    public static void validate(Person p) {
        p.validate();
    }
}


In this example the subclass add new property called employeeCode and override method validate with additional check for it's own property employeeCode.

In the main method I create 2 objects. The first one is object of type Person and the second one is object of type Employee.

When validate person it is ok because all precondition is ok but for the employee it will throw an IllegalArgumentException because it does not match precondition

  1. Does Employee violate LSP due to add new validation on employeeCode?
  2. If 1 is yes, how can I refactor it to avoid violate LSP?
  3. If 1 is no, if I change exception from IllegalArgumentException("Employee code can not be null") to another exception NullPointerException. So does it violate LSP due to introduce new exception type in subtype (which super type does not have)?
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
DXT
  • 23
  • 5
  • 1
    In order to answer this question we would need to know the complete contract of the `validate()` method. In languages like Java, the method signature forms only part of the [contract](https://en.wikipedia.org/wiki/Liskov_substitution_principle#Principle). What are the preconditions, postconditions, and invariants of this method? – jaco0646 Sep 23 '21 at 23:15
  • @jaco0646 invariant of this method is : age must not be null or <=0. employee code can not be null.post condition is method does not throw any exception after validate – DXT Sep 23 '21 at 23:50

1 Answers1

1
  1. No, instances of Person and Employee have exactly the same range of behaviours when validate is called. That is, calling it will either result in an IllegalArgumentException being thrown, or it won't, so no code which is calling validate and correctly handling the result of calling it on Person will fail to correctly handle the result of calling it on Employee.

  2. n/a

  3. In my opinion: As both IllegalArgumentException and NullPointerException are unchecked exceptions, they don't form part of the contract of validate, which is entitled to throw any subclass of RuntimeException it feels like. A better design would have throws ValidationException as part of the signature of validate.

As @jaco0646 says in their comment above, Java doesn't allow you to formally specify everything about a method.

Suppose I write another subclass of Person and decide that my validate() implementation will actually fix any invalid values by setting them to defaults. As we don't expect the validate() method to mutate the object (even though we have no formal way of saying that), that would violate LSP.

tgdavies
  • 10,307
  • 4
  • 35
  • 40
  • Do you mean behavior does not change for all properties that inherit from superclass (in this example age and name from Person) ? When validating an Employee with employeeCode is null it will throw an exception Does it called breaking the application as this article https://stackify.com/solid-design-liskov-substitution-principle – DXT Sep 23 '21 at 23:06
  • See clarification above – tgdavies Sep 23 '21 at 23:09
  • 1
    "When validating an Employee with employeeCode is null it will throw an exception" as will calling validate on a `Person` with a `null` `age`. Both behave the same. – tgdavies Sep 23 '21 at 23:12