Implementing multiple equals methods for a single class - java

I have a class say
class Dog {
String name;
int age;
String collarType;
}
I have already overridden equals method to compare all 3 fields to determine if 2 dogs are equal.
Now I have a situation where I have 2 Dog objects, and they can be equal even if the collarType of the 2 Dog instances are different. Therefore, I can't use my equals method.
Is writing a comparator with the new schema(compare only name and age) and checking if compare(Dog d1, Dog d2) returns 0 the best way to go about it?

Equality is subjective to the user, as you have noticed. However, the only actual equality which is 'correct' should be the one on your object (and it's probably a full equivalence, since it would be weird to have a less strict version be correct). This equals should say that there is impossible, in any way imaginable, under any circumstance, object 1 and object 2 are different if they do equal.
Other ('subjective') equals methods should be achieved through a comparator, and yes, I do think that you should use a comparator. Moreover, you should name that comparator appropriately (like NameAndAgeComparator), to be distinct between, since you might also envision a DogRaceComparator and a SizeComparator to order by size.
You could (possibly) give your comparator a static equals method for ease, which would allow you to refer to it with static imports like equals(dog1,dog2). But that is just my 2 cents

Your class should look like this:
class Dog
{
final String name;
final Integer age;
final String collarType;
public boolean equals(Object other)
{
return this.name.equals(other.name) &&
this.age.equals(other.age) &&
this.collarType.equals(other.collarType);
}
}
That puts your question in a starker category if any of these attributes are mutable.
If any of those fields are not relevant for equals then that should be a different Type.
public interface Named() { public String name(); }
public interface Aged() { public String age(TimeUnit timeUnit); }
public interface Collarable { public String collarType(); }
Then you have:
public class Dog implements Named, Aged, Collarable { /* brevity */ }
and provide natural Comparator<Named>, Comparator<Aged> and Comparator<Collarable> implementations of these interfaces.

Implement equals and hash code if you put it to any kind of Map or Set.
Implement Comparable interface if you want to sort them but it should be compatible with the implemented equals and hashcode methods.
Use Comparator implementation if you want to sort/compare the list by other field(s) than your equlas and hashcode method.

Related

Java clone() inquiry

Having written the code below, I was wondering why clone() doesn't return the same hashcode for each additional instance. Am I doing something wrong?
public class Accessor implements Cloneable {
public static void main(String[] args) {
Accessor one = new Accessor();
Accessor two = one.clone();
System.out.println("one hahcod " + one.hashCode()
+"\ntwo hashcode " + two.hashCode());
}
public Accessor clone(){
try{
return (Accessor)super.clone();
}
catch (CloneNotSupportedException err){
throw new Error("Error!!!");
}
}
}
Since Accessor does not override hashCode, you will get the default implementation of Object.hashCode. This has implementation-defined semantics but will basically cast the address of the object to an integer, such that distinct object instances will have different hashCodes.
See What is the default implementation of `hashCode`? for more information on the above.
Note that if you are going to implement hashCode, you should also implement equals. For a good reference on equals and hashCode, read Joshua Bloch's Effective Java (or see Best implementation for hashCode method)
Because it is a different object. You are invoking the cloning inherited from Object in this case. For each new object you will have a different dashcode. If you open the source code of Object in java what you will find there is the following:
public native int hashCode();
public boolean More ...equals(Object obj) {
return (this == obj);
}
The key point here is that once you clone an object A clone of B A==B will always return false.
Then if you read the hashcode documentation it states the following :
If two
objects are equal according to the equals(Object) method, then calling
the hashCode method on each of the two objects must produce the same
integer result. It is not required that if two objects are unequal
according to the equals(java.lang.Object) method, then calling the
hashCode method on each of the two objects must produce distinct
integer results. However, the programmer should be aware that
producing distinct integer results for unequal objects may improve the
performance of hashtables.
The clone method creates a shallow copy of your first object but your Accessor class has no instance field and does not override hashCode method, as a consequence the instances of this class get the default behaviour from Object class for hashCode. This behaviour is similar as calling System#identityHashCode with your object as parameter.

in what way is a comparator superior to comparable?

"How will you sort collection of employee objects by its id or name". For that we can use two interfaces, i.e., Comparator and Comparable.
seems this is one of the common interview questions
But I don't see a reason why I should use both for sorting employee objects
I have been thinking on what comparator accomplishes that Comparable cannot do.
I understand that if the objects (instance variables that is compared upon) have natural ordering then comparable is the right choice.
but if custom ordering is needed (eg string length) then one could write a comparator.
my point here is comparator is only needed by the client if he wants to sort the data by some other criteria.
For example, I would implement an Employee class to sort by id using comparable interface.
but if the client wants to sort Employee objects by String(name), he would implement comparator either as a concrete class or anonymously in sorting.
Is there anything I am missing here?
For example, In the following code, for the Person object, my compareTo method, compares the age and sort it
In the compare method, I use String length (name of the person) for sorting. In theory, I could accomplish both in the compareTo method as I have implemented below.
lastly, are there any added benefits of one of the following over other
I have implemented comparator in two ways
1. as a static method which is commented out
2. as anonymous object(?) in the main method which is commented out
3. make a new class that implements comparator and call the instance of that class in collections.sort() -- this I have not done here
(The commented-out parts of the code works. They are just different implementations)
mport java.util.Collections;
import java.util.Comparator;
import java.util.*;
public class PersonComparator implements Comparable{
private String name;
private int age;
public PersonComparator(String name, int age) {
this.name = name;
this.age = age;
}
#Override
public String toString() {
return "name=" + name + ", age=" + age;
}
/*#Override
public int compareTo(Object obj) {
if (!(obj instanceof PersonComparator)) {
throw new ClassCastException("Invalid object");
}
PersonComparator p2 = (PersonComparator)obj;
return this.age-p2.age;
}*/
/*Alternative CompareTo that checks for both age and name*/
public int compareTo(Object obj) {
if (!(obj instanceof PersonComparator)) {
throw new ClassCastException("Invalid object");
}
PersonComparator p2 = (PersonComparator)obj;
if (this.age!=p2.age){
return this.age-p2.age;
}
else {
return (this.name.length()-p2.name.length());
}
}
/*public static Comparator nameLengthComparator
= new Comparator() {
#Override
public int compare(Object obj1, Object obj2) {
if (!(obj1 instanceof PersonComparator) || !(obj2 instanceof PersonComparator)){
throw new ClassCastException("Invalid object");
}
else {
PersonComparator p1 = (PersonComparator)obj1;
PersonComparator p2 = (PersonComparator)obj2;
return p1.name.length()-p2.name.length();
}
}
};*/
public static void main(String[] args){
PersonComparator p1 = new PersonComparator("Alexander", 45);
PersonComparator p2 = new PersonComparator("Pat", 27);
PersonComparator p3 = new PersonComparator("Zacky", 45);
PersonComparator p4 = new PersonComparator("Rake", 34);
List<PersonComparator> list = new ArrayList<PersonComparator>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
System.out.println("Before sorting "+ list);
Collections.sort(list);
//System.out.println("After sorting by age "+ list);
//System.out.println("Before sorting "+ list);
//Collections.sort(list, nameLengthComparator);
System.out.println("After sorting by name length "+ list);
/*Collections.sort(list, new Comparator<PersonComparator>() {
#Override
public int compare(PersonComparator p1, PersonComparator p2) {
return p1.name.length()-p2.name.length();
}
}
);*/
System.out.println("After sorting by name length "+ list);
}
}
Thanks
Comparable interface
The Comparable interface defines a type's natural ordering. Suppose you have a list of String or Integer objects; you can pass that list to
Collections.sort(list);
and you will have a sorted list. How? Because String and Integer both implement Comparable interface and the implementations of Comparable interface provide a natural ordering. Its like the class definition saying - "If you find a collection of objects of my type, order them according to the strategy I have defined in the compareTo method".
Now when you define your own type, you can define the natural ordering of the objects of your class by implementing the Comparable interface. See the Java documentation for more information on object ordering.
Comparator interface
The Comparator interface describes how to define custom strategies for object ordering. Suppose we have a simple Person type as below:
public class Person {
String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Now, by implementing the Comparator interface, you can write different strategies to order the instances of your Person type. For example, consider the two strategies for ordering Person objects given below:
class StrategyOne implements Comparator<Person> {
#Override
public int compare(Person p1, Person p2) {
return p1.getName().length() - p2.getName().length();
}
}
class StrategyTwo implements Comparator<Person> {
#Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
Here, StrategyOne will order Person objects based on the length of their names, and StrategyTwo will order Person objects based on lexicographic ordering of their names.
The ways to implement Comparator
As you can see, the concrete strategy classes are stateless, hence all instances are functionally equivalent. So, we just need a single instance of any concrete strategy class. Thus, it should be a singleton. Using anonymous classes will create a new instance each time the call is executed. Consider storing the object in a private static final field and reusing it by using static factory methods to access them [Effective Java]. For example, you can reuse the above two concrete strategies as below:
class Strategies {
private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();
private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();
public static Comparator<Person> personNameLengthComparator(){
return PERSON_NAME_LENGTH_COMPARATOR;
}
public static Comparator<Person> personNameLexicalComparator(){
return PERSON_NAME_LEXICAL_COMPARATOR;
}
}
Summary
To summarize, the Comparable interface is used to define the natural ordering of a class, and the Comparator interface is used to define particular strategies for object ordering.
In what way is a comparator superior to comparable?
It is not "superior". It is just that the two interfaces are doing (roughly) the same thing in different ways. In the Comparable case the ordering logic is in the object being ordered. In the Comparator case, the logic is in a different class from the objects being declared.
But I don't see a reason why I should use both for sorting employee objects
The only case where it would make sense to use both would be if you needed to be able to sort the objects into different orders. Then you could declare the relevant classes as implementing Comparable for the "natural" order and use Comparator objects to implement the other orders.
By the way, a comparator probably should not implement Comparable, and vice versa.
If a comparator implements Comparable that implies you are trying to order instances of the comparator object itself ...
Your PersonComparator class is misnamed. It should really be called Person.
Could you clarify one thing in your answer that we have already equals() method from Object class then why the Comparator interface is facilitating the equals() method again?
A number of points:
You still seem to be confusing the purpose of Comparable and Comparator. The equals method on a Comparator object compares the comparator with other comparators!!
The equals method tells you whether two objects are equal ... not which one comes first.
The reason that Comparator overrides equals is solely so that they can clearly document what equals(Object) does when you call it on a Comparator object. (The actual behaviour is entirely consistent with Object.equals(Object) ... but they obviously thought it necessary to do this because programmers were repeatedly getting the semantics of the method wrong.)
In what way is a comparator superior to comparable?
I won't say it is superior but one advantage is that it enables us to write multiple sort sequences. In case of Comparable, you would have to implement that interface by your class which you want to sort and you can write only one sort sequence.
With Comparator, you can make different classes for sort sequences and while sorting, you just pass the Comparator instance to COllections.sort() method.
Consider Employee class which has fields id, firstName and lastName. If you implement Comparable, you can write only one sorting logic in compareTo method.
If you implement Comparator then you can create separate sorting sequences by creating separate classes. e.g. IdSorter, FirstNameSorter and LastNameSorter which gives you way to sort Employee in multiple ways.
Read
Sorting user defined objects with Comparator
Comparable allows you to sort items in a collections based on only one field.Comparator provides the flexibility to compare items based on more than one field
For example.
class Person implements Comparable
{
int age;
String name;
Person(int age,String name)
{
this.age=age;
this.name=name;
}
public int compareTo(Object o1) // Either you can compare according to age or name
{
Person p = (Person)o1;
if (this.age==p.age)
return 0;
else if (this.age>p.age)
return 1;
else
return -1;
}
public int compareTo(Object o) //Based on name comparision
{
return (this.name.compareTo((Person)o).name));
}
public static void main (String args[])
{
List<Person> list = new ArrayList<Person>();
Person o = new Person(12,"Steve");
Person o1 = new Person(13,"Jason");
list.add(o);
list.add(o1);
Collections.sort(list);
}
}
In case of Comparable above, you can sort items either using age or name.But in case of Comparator ,you can sort the items based on more than one field.
class AgeComparison implements Comparator
{
public int compare(Object o1,Object o2)
{
Person s1 = (Person)o1;
Person s2 =(Person)o2;
if (s1.age==s2.age)
return 0;
if(s1.age>s2.age)
return 1;
else
return -1;
}
class NameComparison implements Comparator
{
public int compare(Object o1,Object o2)
{
Person s1 = (Person)o1;
Person s2 =(Person)o2;
return (s1.age.compareTo(s2.age));
}
}
To use Comparator, you have to pass the list and the instance of class you have to use.
Collections.sort(list,new NameComparison());
Collections.sort(list,new AgeComparison());
In a nutshell, the advantage of Comparator is the flexibility to sort the list based on more than one field of the object.
In general, use Comparable when the ordering is "obvious". E.g., for Strings you use alphabetical, for numbers you use numeric order. Note that a Comparable object can only implement a single compareTo() method, so you only get one option - the "natural", "obvious" option. The advantage is that it is simple and client code doesn't have to do any extra work to compare things.
Use Comparator if the ordering is less obvious, or you might want to have multiple options. For example, a Book might get sorted by Title, Author, ISBN, etc. You could have three different Comparators to handle those three cases. You might want to sort Strings by some unusual order, e.g. a special case for a foreign language, ignoring capitals, etc.
Also, if the Objects you are sorting do not implement Comparable, or you are mixing types that do not like to compare to each other (in general, this is to be avoided, but perhaps you want to be able to compare Books and Authors in a single list in some special case) you need to use Comparator.
Here you go... I have already written a lot on this clarification aided with pictures and explanations.
Please find the link below:
Comparable and Comparator
One think you can always remember and that is "they can’t be used interchangeably"
If you are using comparator , you just need to add one comparator class and pass it to Collections.sort() method along with List object no other change in existing code.
but if you implement comparable interface you will have to change code of all the model/bean classes to override compareTo() method.
so for Loose Coupling comparator is better.

How to sort a list of class objects on one of its fields?

I have a list of DataPoint objects. The class definition is:
public static class DataPoint
{
public Comparable X;
public Comparable Y;
public Comparable Z;
public String text;
...
}
"list" is an ArrayList of DataPoint objects. How do I sort list only on the X value? Would Collections.sort(list, comparator) be used here?
Yes, you should create specific comparator for each field. Example:
Comparator<DataPoint> compByX = new Comparator<DataPoint>() {
#Override
public int compare(DataPoint left, DataPoint right) {
return left.X.compareTo(right.X);
}
};
Collections.sort(list, compByX);
You have two choices:
implement Comparable<DataPoint> for your DataPoint class
write a custom comparator that implements Comparator<DataPoint> and then use Collections.sort
First solution is meaningful if you want to give a natural ordering on your objects (which will be the most used one). Usually it's the one you use first while you use comparators just when you need additional orderings.
They both behave in the same way but Comparable<T> is inherently attached to the object as it is its default comparison algorithm. Whenever sorting is involved the default one will be used unless you specify another one.
class DataPoint implements Comparable<DataPoint> {
#Override
public int compareTo(DataPoint o) {
return X.compareTo(o.X);
}
}
Mind that when you need to compare objects you usually need also other operations on them so take care of overriding hashCode() and equals(Object o). The latter is used in sorting as documentations states:
The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. ... It is strongly recommended (though not required) that natural orderings be consistent with equals.
This means that if you just compare X variable then two different DataPoint objects with same X will be considered equal with respect to compareTo. This can lead to strange situations.

Implementing bidirectional relations between objects of the same class

I have to implement a class whose instances have a bidirectional relation to each other. For example I have the class FooBar which should offer the method sameAs(FooBar x) and maintain a Set for each instances containing its equivalent instances. So if I call foo.sameAs(bar), the Set in foo should contain bar and vice versa. Invoking bar.sameAs(foo) doesn't work, of course.
For clarifiction: the instances of this class are only semantically equal. equals should still return false.
The solutions I've come up with is either to implement a private method internalSameAs(FooBar x) which is invoked from sameAs(FooBar x) or to use a static method sameAs(FooBar x, FooBar y).
Solution 1:
class FooBar {
Set<FooBar> sameAs = new HashSet<FooBar>();
public void sameAs(FooBar x) {
this.internalSameAs(x);
x.internalSameAs(this);
}
public void internalSameAs(FooBar x) {
sameAs.add(x);
}
}
Solution 2:
class FooBar {
Set<FooBar> sameAs = new HashSet<FooBar>();
public static void sameAs(FooBar x, FooBar y) {
x.sameAs.add(y);
y.sameAs.add(x);
}
}
Which one would you prefer and why? Or is there another way I didn't think about?
The naming you've used is confusing. sameAs sounds as though it's a test which should return a boolean, but from your code it seems it would be more appropriately named declareSameAs. When you call foo.sameAs(bar), you're declaring that foo and bar are the same, not doing a test, correct?
The problem is that with your code you can declare
x.sameAs(y);
y.sameAs(z);
but it won't be the case that x is the same as z, which is presumably not what you want (and if it is what you want, you definitely need to change the method name).
It seems to me you want to divide your instances into sets, and have each instance keep a reference to the set it's in (not to a separate set internal to the instance). When you make a new declaration that two instances are the same, you need to combine the sets, and ensure all affected instances have a reference to the combined set.
are you flexible with the data structures to be used? If so you could use a Multimap (from Guava Collections) that is static amongst all the instances of the class FooBar. In that Multimap you can have the keys as FooBar references (or a unique id if you have one) and the values would be the references (or id.s) of the FooBars that have the sameAs relation.
Maybe there's a different way: sameAs sounds pretty similiar to equals. If we do not need equals for something else, then I'd simply implement the equals method on FooBar so that we simply do a
if (foo.equals(bar))
System.out.println("We're equal (aka: 'equivalent/the same')");
In this case, we do not need any set - just a rule to determine, if two instances are equal.
You could store the sameness information in a separate datastructure outside of those classes. A central map could do the job:
HashMap<FooBar, Set<FooBar>> sameFooBars;
If you have "same" objects, simply add them to the map:
public static void addSameObjects(FooBar foo1, FooBar foo2) {
Set<FooBar> set = getMap().get(foo1);
if (set == null) {
set = new HashSet<FooBar>();
getMap().put(foo1, set);
}
set.add(foo2);
// serious implementation avoid code duplication...
set = getMap().get(foo2);
if (set == null) {
set = new HashSet<FooBar>();
getMap().put(foo2, set);
}
set.add(foo1);
}
And the test:
public static boolean isSame(FooBar foo1, FooBar foo2) {
if (getMap().get(foo1) == null)
return false;
return getMap().get(foo1).contains(foo2);
}
Do you really need to maintain a list of equivalences in ALL objects? If possible I would separate the set of equivalences from the objects themselves. This will be easier to maintain.
Then you can use the multimap of #posdef or more simply a Map> to stay with standard JAVA API.
Your "bidirectional" samesAs(...) method sounds like Object.equals(...), which, according to javadoc is a "an equivalence relation on non-null object references". If this is what you want, then you just have to override equals in your class.
I'm a bit lost when you say that "FooBar shouldmaintain a Set for each instances containing its equivalent instances". If you want to build equivalent classes for FooBar objects, then I think it's a good idea to use a java Collection to represent them, and more precisely a Set.
Here is a quickly hacked example:
public class FooBar {
#Override
public boolean equals(Object other) {
// do whatever fancy computation to determine if
// the object other is equal to this object
}
}
and for the equivalent class:
#SuppressWarnings("serial")
public class FooBarEquivalentClass extends HashSet<FooBar> {
#Override
public boolean add(FooBar e) {
if (isEmpty())
return super.add(e);
else if (e.equals(iterator().next()))
return super.add(e);
else
return false;
}
}
"same as" but not "equal to" sounds like you should be using Comparable.
I think it makes more sense to implement compareTo() or sameAs() as an instance method rather than a static since you will always need two real instances to do any comparison.
Sounds like what you want are to separate the equivalence groups from the object instances.
Make a Map<FooBar, Set<FooBar>> and note that when you lookup an object the set will include itself.

How to compare Classes and Inherited Classes in Java

I have two classes - Task (which implements Comparable) and DeadlinedTask (where DeadlinedTask extends Task). And for each of them I have written an overloaded compareTo function (each has compareTo(Task) and compareTo(DeadlinedTask)).
The idea is that I can sort normal Tasks by category, and DeadlinedTasks by deadline, but I also want all of the DeadlinedTasks to be sorted above the Tasks.
When I call Collections.sort(myListOfTasks) on a list of only Tasks (no DeadlinedTasks), everything works like a charm.
However when I have a list of both Tasks and DeadlinedTasks, the objects change order, but they are not fully sorted.
I have tried returning numbers other than 1 on the interclass compares (1, 1000, 1000000 all did the same thing). Is there any way to do this through compareTo and Collections.sort, is there a different java functionality I can use, or do I have to write my own search function (as a Comparator?)?
Task compareTo Methods:
public int compareTo(Task other){
if(this.GetCategory().compareTo(other.GetCategory())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetCategory().compareTo(other.GetCategory());
}
public int compareTo(DeadlinedTask other){
return 1;
}
DeadlinedTask compareTo Methods:
public int compareTo(Task other){
return -1;
}
public int compareTo(DeadlinedTask other){
if(this.GetDeadline().compareTo(other.GetDeadline())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetDeadline().compareTo(other.GetDeadline());
}
Thanks for any help
... or do I have to write my own search function (as a Comparator?)?
Yes. I think that's the best way.
The normal way to handle equals and compareTo is to return false (for equals) or throw ClassCastException (for compareTo) if the arguments actual type doesn't match the actual type of this.
If you try to implement equals or compareTo for subtypes, you can easily create semantic anomalies such as:
a.equals(b) and b.equals(a) returning different values, or
a.compareTo(b) and b.compareTo(a) returning inconsistent values.
Avoiding those anomalies would entail making the supertype aware of the subtype. That is a bad from a design perspective because it restricts your ability to create more subtypes in the future.
For use-cases where you need to implement a rule that orders instances of two or more different classes, a Comparator is the best solution.
Per class, only one compareTo method can be used to implement the Comparable interface. If you use Comparable without generics, then this is
public int compareTo(Object o)
If you're using generics, e.g. Comparable<Task>, then it's
public int compareTo(Task o)
Your compareTo(DeadlinedTask o) method will be ignored concerning the Comparable<Task> interface. It just "accidentally" has the same name, but it's an independent overloading.
(By the way, it's not possible to implement both Comparable<Task> and Comparable<DeadlineTask>).
So what you'll have to do instead, is change your Task.compareTo(Task o) method to use instanceof (it has to use runtime information after all). I agree with Stephen, that it would even be better to write a Comparator.
Comparable defines a natural order for all instances of a class. So if DeadlinedTask should always come before Tasks, then the compareTo method should implement it.
You should not redefine compareTo in DeadlinedTask, because this would break the contract of anti-commutativity : if (t1.compareTo(t2) > 0), then t2.compareTo(t1) < 0.
I would thus completely avoid to implement Comparable in the Task class, and use a dedicated comparator when sorting a collection of tasks. If you really want your task to implement Comparable, than you need to make its implementation depend on the existence of DeadlinedTask (which is not very OO) :
public class Task implements Comparable<Task> {
// ...
public final int compareTo(Task t) {
if (this instanceof DeadlinedTask) {
if (t instanceof DeadlinedTask) {
return ((DeadlinedTask) this).getDeadline().compareTo(((DeadlinedTask) t).getDeadline());
}
else {
return -1;
}
}
else if (t instanceof DeadlinedTask) {
return 1;
}
else {
return this.category.compareTo(t.category);
}
}
}
Note that Java uses a lower-case letter at the beginning of methods (getDeadline(), and not GetDeadline()), and that you don't need to use getters to access private properties of your own class.
In addition to what StevenC have said, if you know in advance that you will have a hierarchy of value objects, you can check whether the class of the argument of the compareTo() method is a subtype of the class of the object and if yes, reverse the comparison, so you will always have the child comparing against the parent:
public boolean compareTo(Object o) {
// check for null
boolean isSubtype = getClass().isAssignableFrom(o.getClass()) && getClass()!=o.getClass()
if (isSubtype) return -((/*cast to this type*/) o).compareTo(this);
}
This way, the comparison remains consistent and the base type does not to be aware pf each individual subtype, but just that subtypes exist.
Yes it seems a comparator is the simplest (& cleanest way)
But you can simply delegate te bulk ot the work to the compareTo(...) methods you have already written, all you really need to add is code to handle comparison between the sub and super classes:
public int Compare(Task t1, Task t2) {
if (t1 instance of DeadlinedTask && !(t2 instanceof DeadlinedTask))
return 1;
else if (t2 instance of DeadlinedTask && !(t1 instanceof DeadlinedTask))
return -1;
else
return t1.compareTo(t2);
}
but it just occured, how are you declaring the classes? do you include Comparable in the implements clause of the Task class and visa versa? if not, then perhaps when the lhs object is a Task, then only compare(Task) gets called ?? otherwise you need to have both in the implements clause ie:
class Task implements Comparable<Task>, Comparable<DeadlinedTask>
The magnitude of the value returned will not change anything, ie returning 1 and 1000000 is exactly the same, as tests are only < 0, > 0 and == 0 (this contract IS specified in the docs for the Comparator interface. I used to tell students trying to remember what return values mean, to imagine comparing ints, then we could just write:
int compare (int a, int b) { return a - b; }

Categories

Resources