I have a base BusinessObject abstract class which implements Comparable by comparing their long id fields. Now imagine I extend it with, say, Person and then I extend Person with Worker. So we have:
BusinessObject < Person < Worker
So now I override the compareTo(BusinessObject) in business object in Person (compare the names) and Worker (compares the job name, then person name).
Now I do something like:
List<BusinessObject> collection = new ArrayList<>();
collection.add(new Worker(1L, "Steve", "janitor"));
collection.add(new Worker(2L, "Mark", "plumber"));
collection.add(new Person(3L, "Dave"));
Collections.sort(collection);
System.out.println(collection);
By logging I can see the calls that are made:
So, it means sorting methods are mixed which is obviously not good. So what is the correct way to implement Comparable with inheritance so that the method called depends on the generic type of the collection:
Here's my code:
public abstract class BusinessObject implements HasId, HasText, Comparable<BusinessObject> {
protected @Nullable Long id;
public BusinessObject(@Nullable Long id) {
this.id = id;
}
@Override
public @Nullable Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public int compareTo(@NonNull BusinessObject o) {
System.out.println("compareTo BusinessObject");
return Long.compare(id, o.id);
}
@Override
public String toString() {
return String.format("BusinessObject#%d", id);
}
}
public class Person extends BusinessObject {
protected final String name;
public Person(@Nullable Long id, String name) {
super(id);
this.name = name;
}
@NonNull
@Override
public String getText(Context context) {
return null;
}
@Override
public String toString() {
return String.format("Person#%d (%s)", id, name);
}
@Override
public int compareTo(@NonNull BusinessObject o) {
if (o instanceof Person) {
System.out.println("compareTo Person");
Person po = (Person) o;
return name.compareTo(po.name);
}
else {
return super.compareTo(o);
}
}
}
public class Worker extends Person {
protected final String job;
public Worker(@Nullable Long id, String name, String job) {
super(id, name);
this.job = job;
}
@Override
public String toString() {
return String.format("Worker#%d (%s-%s)", id, name, job);
}
@Override
public int compareTo(@NonNull BusinessObject o) {
if (o instanceof Worker) {
System.out.println("compareTo Worker");
Worker wo = (Worker) o;
return String.format("%s%s", name, job).compareTo(String.format("%s%s", wo.name, wo.job));
}
else {
return super.compareTo(o);
}
}
}
I think it is simply not possible to do, what you want to do. The generic type does and can not have any effect on the methods used from the items.
But you might be able to solve your problem. Just do not use the compareTo methods but instead implement Comparators as needed.
There is a version of Collections.sort() which takes a Comparator as parameter.
Obviously you want to sort the items with different sort criterias depending on the list you are using. So I think the Comparator would be the best solution.