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:
- Worker.compareTo()
- Person.compareTo()
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:
- if collection is a List then always use BusinessObject.compareTo()
- if collection is a List then always use Person.compareTo()
- if collection is a List then always use Worker.compareTo()
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);
}
}
}
Comparableyou do not have any information about the other elements of the list (except the elements to get compared with). So you cannot differ which comparison to useComparable, you know thatCollectionscan also be sorted withComparator? That should help you achieve want you want.