im trying to build this abstract method within an abstract class and using it in one of my subclasses and it's return type shall be a List / ArrayList. I'm really struggling with the syntax, and most of the previous asked question are not referring to my special issue.
Most important for me to describe it in easy words.
Here the abstract class*
private int employeeID;
private String name, sID, department;
private double salary;
// Constructor (empty)
public Employee() {
}
// Constructor => Every Employee has at least this attributes => Subclasses may have additonal attributes
public Employee(int employeeID, String name, String sID, double salary, String department) {
this.employeeID = employeeID;
this.name = name;
this.sID = sID;
this.salary = salary;
this.department = department;
}
// Getters and Setters below
//.......
// Abstract method with generic list as return type
public abstract <T> List<T> read(String path);
Here is the Subclass
public class Clerk extends Employee {
public Clerk() {
}
public Clerk(int employeeID, String name, String sID, double salary, String department) {
super(employeeID, name, sID, salary, department);
}
@Override
public <T> List<T> read(String path) {
// TODO Auto-generated method stub
String line = null;
List<Clerk> clerk = new ArrayList<Clerk>();
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
reader.readLine();
while ((line = reader.readLine()) != null) {
String[] clerkValues = line.split(";");
clerk.add(new Clerk(Integer.parseInt(clerkValues[0]), clerkValues[1], clerkValues[2], Double.parseDouble(clerkValues[3]), clerkValues[4]));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return clerk; // Error here,Type mismatch: cannot convert from List<Clerk> to List<T>
I'm always getting an error. And I don't want to build the superclass (Employee) as a generic Class like =>
public abstract class Employee <T>{
// Code here
}
Thank you in advance for your support.
You can declare the method in Employee
like this:
public abstract List<? extends Employee> read(String path);
You could translate this to English like this "read returns a list of some unknown type of employee". You'll be able to iterate the list and otherwise read from it, and treat elements as Employee
. You won't be able to add new Employee
elements to the list, or otherwise pass Employee
elements as parameters to methods. For this use case, that is probably fine.
You can override it in Clerk
and the other subclasses with method signatures like this:
public List<Clerk> read(String path)
This works because List<Clerk>
is a subtype of List<? extends Clerk>
, and Java allows you to override a method with the overriding method returning a more specific type than the method it overrides.
If you then call read
on a reference of type Clerk
, the return type will be List<Clerk>
, as expected.
This would not work if the Employee
class returned List<Employee>
instead of List<? extends Employee>
, because List
is invariant and therefore List<Clerk>
is not a subtype of List<Employee>
. To understand why that is, consider that a List<Employee>
does allow you to add new Employee
elements to it. If the actual type of the list were List<Clerk>
, but the compiler allowed adding other types of Employee
to the list that are not instances of Clerk
, it would violate the expectation that the list only contained Clerk
elements, and likely lead to runtime errors.
I do agree with Gene's comment that it doesn't make much sense to have these methods on instances of Employee
and Clerk
. Perhaps having an EmployeeReader
interface and ClerkReader
implementation would be better.