How to compare Classes and Inherited Classes in Java - 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; }

Related

Java incompatible type: T cannot be converted to My Class

I am trying to write a custom comparator method to compare different things in my custom class Customer and when I compile I get incompatible types: T cannot be converted to Customer. DataChecker.compare() is run from a generic method but I know that whenever it is run T will be a type that is defined in the overloaded DataChecker.compare() method. Below is my code:
public int compare(Customer cust1, Customer cust2, String sortBy){ //Comparator interface for customers
if (sortBy == "ID"){
if (cust1.getID() < cust2.getID()){
return -1;
} else if (cust1.getID() == cust2.getID()){
return 0;
} else {
return 1;
}
}
throw new IllegalArgumentException("Not a valid compare for Customer: " + sortBy);
}
The method is called here (left and right are T[ ] but when this is run T will be one of the types - e.g. Customer - that is defined in DataChecker.compare()):
if (DataChecker.compare(left[i], right[j], sortBy) <= 0){
input[k++] = left[i++];
} else {
input[k++] = right[j++];
}
Thanks in advance
T will be a type that is defined in the overloaded DataChecker.compare() method.
That is almost entirely useless. In java, overloads are completely different methods that have absolutely no relationship whatsoever; code is interpreted at compile (write) time to one of the overloads, and that particular method is then hardcoded in your class file. At runtime, override is applied dynamically (java is fully dynamically dispatched), but the 'full name' of each method includes the complete type it is, the name, and the types of each parameter, and the return type.
In other words, it is not possible to tell the compiler: Just figure it out; call the right compare method, and I guarantee you it will be there. I guess, if not, throw a ClassCastException or something.
If you think about it for a while, this starts to make some sense. String is a subtype of Object, but also of CharSequence, and also of Serializable. Imagine you had compare(Serializable a, Serializable b) as well as compare(CharSequence a, CharSequence b). Which one should be called?
The only solution, then, is to either have no overloads, or to create a single method that is the entrypoint for this dynamic process, which does its own, handrolled dynamic dispatch, based on e.g. a Map<Class<?>, BiPredicate<?>> or a bunch of instanceof checks.

Custom equals outside of the class definition

Is there a way to write a custom equals method compactly when trying to compare two objects but not relying on those objects' internal equals() method? For example, if I had two Foo objects like so:
public class Foo {
int id;
String name;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Foo item = (Foo) o;
return id == item.id && listId == item.name;
}
}
But, in a use case for the foo objects, lets say I just want them equated by their id. Keep in mind this is a toy example and the real use case has many more fields so I may have an object with 6 fields all being used in the overridden equals method but may want to only use 3 of them to do an equals outside the class when comparing two objects.
List<Foo> objType1;
List<Foo> objType2;
Compare the two lists and assert each Foo object is equal but only use a subset of the fields of Foo in the comparison. I dont want to touch the actual Foo object in any way. How can i do this outside of asserting by handing that each field I am interested in is equal?
The complexity of the solution really depends on the use case. Universally - no, one cannot do it without direct bytecode manipulation.
A lot of Java APIs allow using custom Comparator as an option. E.g. if you want to compare two lists of Foo's with custom comparator:
List<Foo> list1 = ...
List<Foo> list2 = ...
Comparator<Foo> c = Comparator.comparing(Foo::getId);
boolean equal = list1.size() == list2.size() &&
IntStream.range(0, list1.size())
.allMatch(i -> c.compare(list1.get(i), list2.get(i)) == 0);
Note, this solution does not check for list1 or list2 being null's and assumes your Foo has standard getters like getId(). Also, if you don't deal with lists but with abstract iterable collections, you might want to look into zip implementations. Comparator.comapring() is chainable like this Comparator.comparing(Foo::getId).thenComparing(Foo::getAttrX).thenComparing(Foo:getAttrY)...; which is fairly convenient and readable.
Another option you might want to look at to customize equals() for a variety of cases is to use Proxy.newProxyInstance() with your custom equals override, i.e. auto-create proxy wrapper around your instances when filling collections etc.
UPDATE
Using Comparator.comparing().thenComparing()... chain might seem to be tricky. It helps to understand that lambdas for these functions need to extract either primitives or Comparable descendants (i.e. implements Comparable and has compareTo() method) from the given top level object reference - in our case, Foo. If Foo would have a Bar getBar() accessor that has to be included into comparison, then either go down to the primitive - .thenComparing(f -> f.getBar().getName()) or make Bar implement Comparable. Don't forget to treat nulls properly, if you go the route of custom lambda functions - which is, sometimes, a challenge on it's own.
The positive of an approach in this answer is that Comparator defines a total order over the set of objects stored in the lists. The negative of this approach is, this total order is not really needed for simple comparison - if it is really all you need. In some cases, writing a good old for loop and doing all the comparisons "manually" might be less confusing. From experience, in most cases having an order is beneficial, if not now, then in the next release.
You could add some extra field(s) to your Foo class that will be used in your overriden equals method to specify which fields should be used to deteremine whether two instances are equal. You could then set those fields before comparing. Then your equals method might contain:
if ( useFiledA ) {
if ( this.a != item.a ) return false;
}
if ( useFiledB ) {
if ( this.b != item.b ) return false;
}
// etc.
return true;
implements Comparable from your classes. then write the compareTo method. iterate ocer the first list and call colpareTo over all the elements of the second list

Comparing Generics

Hi I'm trying to compare two objects using by extending the generics with the interface Comparable:
public class Tree<K extends Comparable<K>>
{
Node<K> treeNode;
// Some initialization stuff
public void test(Node<K> node)
{
// Some code
// This line fails
if(node.getKey() > treeNode.getKey())
{ ... }
// Rest of the code
}
}
public interface Node<K extends Comparable<K>>
{
// Some code
public K getNode();
// Some more code
}
But some reason, the compare operator is not recognized. I thought that by extending comparable I could begin using such an operator. What am I doing wrong. Thanks.
Comparable doesn't let you use < or > to compare, just like List and Map don't let you use [] for item access. You need to call the compareTo method, which will indicate the result of the comparison by returning an integer less than, greater than, or equal to 0.
There is no operator overloading in Java. Therefore, you can't use > even if the objects implement Comparable. You have to use compareTo() instead.

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.

Categories

Resources