Erasure of method is the same as another method in type

Your problem is due to type erasure: the parametrized type information in your Lists is erased at runtime, hence the methods have a virtually identical signature and your code cannot compile.

In order to solve your problem, here's a solution that generalizes the common features of Developer and Student, thus only requiring one getArrayOfTimespan method for both given parametrized types:

// common interface to Student and Developer classes
interface Datable {
    LocalDate getStartDate();
}
// dev class implementing "datable"
class Developer implements Datable {
    @Override
    public LocalDate getStartDate() {
        // TODO Auto-generated method stub
        return null;
    }
}
// student class implementing "datable"
class Student implements Datable {
    @Override
    public LocalDate getStartDate() {
        // TODO Auto-generated method stub
        return null;
    }
}
// parameter interpreted as list of super type to both dev and student
private LocalDate[][] getArrayOfTimespan(List<Datable> args)
{
    for (Datable d: args) {
        // TODO something
        LocalDate foo = d.getStartDate();
    }
    // TODO return something
    return null;
}

This is an inherent problem with Java generics. Because generic types are erased at runtime, the jvm cannot decide which version of the getArrayOfTimespan() method to call, as both will have the exact same signature: i.e. getArrayOfTimespan(List<Object> arg1). Hence your error. However if we look beyond the surface of Java generics an it's limitations, the Java compiler is actually telling you that you have a deeper problem.

It seems that both your Student and Developer classes share behaviour to some degree, as both have an identically named method that does the same thing: getStartDate(). This would suggest that you could declare an interface that defines this common behaviour, e.g. "Startable" and then you will only need to define the getArrayOfTimespan() method once for the interface.

interface Startable {
   LocalDate getStartDate();
}

class Developer implements Startable { /* ... */ }

class Student implements Startable { /* ... */ }

class hierarchyValidator {
    private LocalDate[][] getArrayOfTimespan(List<Startable> startables)
    {
        // ...
        LocalDate startDate = startable.getStartDate();
        // ...
    }
}

Continuing on that track you will probably notice that you are doing some copy-pasting between your Developer and Student classes, as I am guessing that they not only share common behaviour, but also common structure (at least a private LocalDate startDate; field). This is always an indicator that you should be extracting your common structure and behaviour into an abstract class. E.g:

abstract class Person {
    private LocalDate startDate;

    public LocalDate getStartDate() {
        return this.startDate;
    }
    // ... etc ...
}

class Student extends Person{}

class Developer extends Person{}

class hierarchyValidator {
    private LocalDate[][] getArrayOfTimespan(List<Person> people)
    {
        // ...
        LocalDate startDate = person.getStartDate();
        // ...
    }
}

This will not only save you a lot of copy-pasting, but it will also make your code clearer to others, and help you to avoid bugs, when you change things in one place and forget about the copies.

So in short: If you realise that you are busy copy-pasting, or if you are having generics trouble, it almost always means you need inheritance. If your classes share common behaviour(i.e. have the same methods) --> use an interface. If your classes share common structure(i.e. have the same fields) --> use an abstract class.

Hope this helps, and good luck!


Generally, a proper way to refactor an interface with identical erasures is to remove the overload by renaming one or both methods:

class HierarchyValidator {
    private LocalDate[][] getArrayOfDeveloperTimespan(List<Developer> developers) {
       //here i have to call some method on developer from list
        LocalDate startDate = developer.getStartDate();
        ....
    }
    private LocalDate[][] getArrayOfStudentTimespan(List<Student> students) {
       //here i have to call some method on student from list
        LocalDate startDate = student.getStartDate();
       .....
    }
}

Now the two methods are no longer overloading each other, letting you proceed with separate implementations.