1

That's my first post and it will be a tough one.

We have a list of Employee class elements, which at the same time can be elements of a 5 types of subclasses: ApprenticeEmployee, SalariedEmployee, CommissionEmployee, CommissionEmployeeWithSalary and HourlyEmployee, all of them contained in the same list employee.

ApprenticeEmployee: doesn't implement calculateSalary()

SalariedEmployee, CommissionEmployee, CommissionEmployeeWithSalary and hourlyEWmployee: implement calculateSalary().

The problem requires calculating the average salary using Stream. I've tried to do it using filter and mapingToDouble. The problem is the downcasting since we are not allowed to use conditionals (if-else) cos it said we are using "declarative language" and conditionals are against its philosophy. How could I downcast the elements depending on their subclass? I've tried with getClass(); but the returned class it's always Employee, not the subclass. The code i've tried it's:

public Double averageSalary() {
    return  employees.stream()
            .filter(employee -> !(employee instanceof ApprenticeEmployee))
            .mapToDouble(employee -> { 
                (employee.getClass()).cast(employee);
                return employee.calculateSalary();
            })
            .average()
            .getAsDouble();
}

calculateSalary() is a method from the interface Payroll, which is implemented by 4 out of the 5 subclasses, all of them but ApprenticeEmployee.

Sorry if I'm not typing the code in the proper way, I'm new to it! Thanks in advance!

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Ketterle
  • 15
  • 3
  • Is there a relationship that is derivable amongst the rest of the classes(other than `ApprenticeEmployee`)? or else how do you think you should be able to cast `CommissionEmployee` object to `SalariedEmployee`? the only thing they bring together is that they have a common parent...other way to put it is, what is the type of `employees` collection? – Naman Apr 18 '21 at 11:03
  • Is there an interface with the method `calculateSalary()` which is implemented by the 4 of 5 classes you have? – Progman Apr 18 '21 at 11:07
  • `Class.cast()` does not change the type of the given argument, so the variable `employee` is still an `Employee` object. However, the `cast()` method returns the given argument as the type of the `Class` object you are using. But in the end, that approach will not be useful for you in this case. – Progman Apr 18 '21 at 11:10

2 Answers2

3

I'd suggest to add an interface, where "calculateSalary" method will be allocated. Let us name it "CanCalculateSalary" for example. This interface will be implemented by all Employee classes which needs to be able to calculate salary. So your code would look like this:

 return employees.stream()
        .filter(CanCalculateSalary.class::isInstance)
        .mapToDouble(employee -> ((CanCalculateSalary)employee).calculateSalary())
        .average()
        .getAsDouble();
Sergey Vasnev
  • 783
  • 5
  • 18
  • 1
    I think you might want to remove the `!` in the instanceof check, because you are *removing* the instances of `CanCalculateSalary` atm. (`.filter(CanCalculateSalary.class::isInstance)` is the more idiomatic way to do this). – Andy Turner Apr 18 '21 at 11:16
  • 1
    `map(CanCalculateSalary::calculateSalary)` should work instead of explicitlity casting. If it does not, `map(CanCalculateSalary.class::cast).mapToDouble(CanCalculateSalary::calculateSalary)` should do – Yassin Hajaj Apr 18 '21 at 11:21
  • Hey Sergey! Thank you very much, it's already implemented and working. I was wrongly approaching to the problem: I was filtering by subclass and you clearly show me I should do it by Interface. Easy like this. – Ketterle Apr 18 '21 at 15:39
0

With the information about the interface declaring your method it’s straightforward.

public Double averageSalary() {
    return  employees.stream()
            .filter(employee -> employee instanceof Payroll)
            .mapToDouble(employee -> ((Payroll) employee).calculateSalary())
            .average()
            .getAsDouble();
}

This has further advantages: It allows for future addition of further Employee subclasses some of which may implement Payroll and some of them may not. You still will not need to modify the code above (but test it again with the new subclasses).

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    Thanks Ole! I should post it at once, but I was a little bit overwhelmed by the problem, sorry! And yeah, it's really working ;) – Ketterle Apr 18 '21 at 18:16