Home > Mobile >  Java - How to get data only from desired classes (stream)?
Java - How to get data only from desired classes (stream)?

Time:10-28

Employee class:

public abstract class Employee extends Person {
    private final Manager manager;
    private final BigDecimal salary;
    
    protected Employee(String firstName, String surname, LocalDate birth_date, Manager _manager, BigDecimal _salary) {
        super(firstName, surname, birth_date);
        manager = _manager;
        salary = _salary;
        if (manager != null) {
            manager.getSubordinates().add(this);
        }
    }

    ...
}

Worker class:

public class Worker extends Employee {
    private final LocalDate employment_date;
    private BigDecimal bonus;
    
    public Worker(String firstName, String surname, LocalDate birth_date, Manager manager, BigDecimal salary,
                  LocalDate _employment_date, BigDecimal _bonus) {
        super(firstName, surname, birth_date, manager, salary);
        employment_date = _employment_date;
        bonus = _bonus;
    }

    ...
}

Manager class:

public final class Manager extends Worker {
    List<Employee> subordinates = new ArrayList<Employee>();
    
    public Manager(String firstName, String surname, LocalDate birth_date, Manager manager, BigDecimal salary,
                   LocalDate employment_date, BigDecimal bonus) {
        super(firstName, surname, birth_date, manager, salary, employment_date, bonus);
    }

    ...
}

Trainee class:

public class Trainee extends Employee {

    private final LocalDate start_date;
    private final short apprenticeship_length;
    
    public Trainee(String firstName, String surname, LocalDate birth_date, Manager manager, BigDecimal salary,
                   LocalDate _start_date, short _apprenticeship_length) {
        super(firstName, surname, birth_date, manager, salary);
        manager.getSubordinates().add(this);
        start_date = _start_date;
        apprenticeship_length = _apprenticeship_length;
    }
}

payrol class:

public final class PayrollEntry {

    private final Employee _employee;
    private final BigDecimal _salaryPlusBonus;
    
    public PayrollEntry(Employee employee, BigDecimal salary, BigDecimal bonus) {
        _employee = employee;
        _salaryPlusBonus = salary.add(bonus);
    }
}

I have to write the function List<PayrollEntry> payroll(List<Employee> employees) {}. As you can see above, only Worker and Manager can have a bonus, Trainee on the other hand don't, but all of them derive from class Employee (BTW I can't change anything in classes hierarchy because it's my assignment and the hierarchy was written by teacher). I'm supposed to use functional programming techniques to write the function, this is my try:

    public static List<PayrollEntry> payroll(List<Employee> employees) {
        return employees
                .stream()
                .map(employee -> new PayrollEntry(employee, employee.getSalary(), ((Worker) employee).getBonus()))
                .collect(Collectors.toList());
    }

I understand why it gives me a ClassCastException, but I don't know of any other way of doing this using stream. I think I can do it with a for-each loop checking each time if it's a Trainee or not, but I would like to know if there's a way of doing it using stream.

CodePudding user response:

Is there any constraint on what should be contents of map()?

otherwise you can have something like:

.map(employee -> {
    if (employee instanceof Worker) {
        return new PayrollEntry()....
    } else {
        return new PayrollEntry()....
    }
})

CodePudding user response:

I'm not sure if it fits the task definition your teacher gave you, but you could expand your map stream operation as follows:

public static List<PayrollEntry> payroll(List<Employee> employees) {
        return employees
                .stream()
                .map(employee -> {
                    // if statements to check type of employee
                    // set some variables for the various fields
                    return new PayroleEntry(...);
                })
                .collect(Collectors.toList());
}

CodePudding user response:

You can use a ternary operator before casting and check if employee is an instance of Worker class. If yes pass the bonus else BigDecimal.ZERO:

employees.stream()
         .map(employee -> new PayrollEntry(employee, employee.getSalary(),
               employee instanceof Worker ? ((Worker) employee).getBonus() : BigDecimal.ZERO))
         .collect(Collectors.toList());

CodePudding user response:

I would create a static constructor for these situations. (Something like PayrollEntry.forEmployee(Employee)) Since you are not supposed to change the original classes you can just place the method somewhere else.

private static PayrollEntry newPayrollEntry(Employee employee) {
    BigDecimal bonus = BigDecimal.ZERO;        
    if (employee instanceof Worker) {
        bonus = ((Worker) employee).getBonus();
    }
    return new PayrollEntry(employee, employee.getSalary(), bonus);
}

public static List<PayrollEntry> payroll(List<Employee> employees) {
    return employees
            .stream()
            .map(Main::newPayrollEntry)
            .collect(Collectors.toList());
}

You can also place the same code in curly brackets, but moving the code into a static method is easier to read.

stream.map(employee -> {
    // Long code
    return payroll;
})
  • Related