I want to compare two Java classes.
class ClassComparator implements Comparator<Class> {
@Override
public int compare(Class arg0, Class arg1) {
return ....;
}
}
I could just compare class names, but I want parent classes be "smaller" than classes derived from them. And I want this less-than relation to be transitive and work for any two classes. (To be honest, in my real problem one class will always be a superclass of another, but I want some general-case code because in theory this can change.)
Maybe this is already done and someone can share a code snippet?
What comes to my mind is: if none of the classes is derived from another, find their two superclasses derived from the common ancestor, and compare their names. (Hmmm, it can even support interfaces, if any object class is greater than any interface.)
Yes we can extend any pojo class to implement both comparable and comparator interface and implement the methods defined by them. I implemented it in sample Student POJO class.
Comparator , represents a component that can compare two objects so they can be sorted using sorting functionality in Java. When sorting e.g a Java List you can pass a Java Comparator to the sorting method. The Comparator is then used to compare the objects in the List during sorting.
Comparator is used in Java to sort objects based on multiple data members (i.e) based on any conditions because Comparable can be used to sort only based on a single data member. Comparator can also be used when we want a sorting order other than the default sorting order of the class.
The Comparator and Comparable interface don't do any sorting, so there is no sorting algorithm there. They just compare two Objects, something you need if you want to sort a list of those objects.
You can also compare classes that are not in one hierarchy by how deep are they and far away from Object class.
class ClassComparator implements Comparator<Class> {
@Override
public int compare(Class arg0, Class arg1) {
boolean arg0assignable = arg0.isAssignableFrom(arg1);
boolean arg1assignable = arg1.isAssignableFrom(arg0);
if (arg0assignable == arg1assignable && arg0assignable) {
return 0;
} else if (arg0assignable) {
return -1;
} else if (arg1assignable){
return 1;
} else {
return compareByDistanceToObject(arg0, arg1);
}
}
private int compareByDistanceToObject(Class arg0, Class arg1) {
int distanceToObject0 = getDistance(arg0);
int distanceToObject1 = getDistance(arg1);
if (distanceToObject0 == distanceToObject1) {
return 0;
} else if (distanceToObject0 < distanceToObject1) {
return -1;
} else {
return 1;
}
}
private int getDistance(Class clazz) {
if (clazz == Object.class) {
return 0;
}
return 1 + getDistance(clazz.getSuperclass());
}
I think you should be able to just do:
new Comparator<Class>() {
@Override
public int compare(Class o1, Class o2) {
if (o1 == o2)
return 0;
if (o1.isAssignableFrom(o2))
return -1;
if (o2.isAssignableFrom(o1))
return 1;
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
}
Your constraints do not yield an ordered set.
class C {}
class B {}
class A extends C {}
Then you have:
EDIT: Since Comparator imposes a total ordering, there is no solution to your question.
EDIT 2: However, if there is no solution within your constraints, you can change them. If your objective is to define a total order among classes, so that a superclass is always less-than a subclass (ignoring interfaces) (i.e. we don't require to compare class names anymore), you can:
I just realized that comparing class names was not a requirement in your question.
Let's take an example:
class C {}
class B {}
class A extends C {}
class D extends A {}
List the hierarchy of each class:
Then, you get a total order:
So, we want to define a total order on classes such that any parent class/interface is "smaller" than any derived class/interface.
The solution is:
Any interface class is smaller than any object class.
to compare two interfaces, we compare the number of their superinterfaces. If they are equal, we compare their names.
to compare two object classes, we compare the number their of superclasses. If they are equal, we compare their names.
Why this is correct. A derived class always have more ancestors than any of its its superclasses. So if we compare the number of ancestors, we guarantee that superclasses go before their descendants. And as to the ordering within the group of classes that have N parents, any ordering will do, alphabetic ordering is ok.
class ClassComparator implements Comparator<Class<?>> {
@Override
public int compare(Class<?> first, Class<?> second) {
int areInterfaces = first.isInterface() ? 1 : 0;
areInterfaces += second.isInterface() ? 2 : 0;
switch (areInterfaces) {
case 1 + 2:
return compareNumbersThenNames(getInterfaceCount(first), getInterfaceCount(second), first, second);
case 0 + 2:
return -1;
case 1 + 0:
return 1;
case 0 + 0:
default:
return compareNumbersThenNames(getAncestorCount(first), getAncestorCount(second), first, second);
}
}
private int compareNumbersThenNames(int f, int s, Class<?> first, Class<?> second) {
if (f-s != 0) {
return f-s;
} else {
return compareByName(first, second);
}
}
private int getAncestorCount(Class<?> objectClass) {
int res=0;
for (Class<?> i = objectClass; i != null ; i = i.getSuperclass()) {
res++;
}
return res;
}
private int getInterfaceCount(Class<?> interfaceClass) {
Set<Class<?>> superInterfaces = new HashSet<>();
addSuperinterfaces(superInterfaces, interfaceClass);
return superInterfaces.size();
}
private void addSuperinterfaces(Set<Class<?>>set, Class<?>interfaceClass) {
for (Class<?> s : interfaceClass.getInterfaces()) {
if (!set.contains(s)) {
set.add(s);
addSuperinterfaces(set, s);
}
}
}
private int compareByName(Class<?> a, Class<?> b) {
int res = a.getSimpleName().compareTo(b.getSimpleName());
if (res != 0) { return res; }
res = a.getName().compareTo(b.getName());
// we do not support different class loaders
return res;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With