Comparator for Java classes?

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 the parent classes to be “smaller” than the classes derived from them. And I want this less than relation to be transitive and work for any two classes. (Honestly, in my real problem, one class will always be a superclass of another, but I need some common code, because theoretically this can change.)

Perhaps this has already been done, and can someone share a piece of code?

What comes to my mind: if none of the classes is derived from the other, find their two superclasses, derived from a common ancestor, and compare their names. (Hmmm, it can even support interfaces if any object class is larger than any interface.)

+6
source share
5 answers

You can also compare classes that are not in the same hierarchy, how deep they are and far from the 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()); } 
+7
source

Your limitations do not give an ordered set.

 class C {} class B {} class A extends C {} 

Then you have:

  • A <B
  • B <C
  • C <A

EDIT: Since Comparator imposes a complete ordering, there is no solution to your question.

EDIT 2: However, if your restrictions do not have a solution, you can change them. If your goal is to determine the general order between the classes, so that the superclass is always smaller than the subclass (ignoring the interfaces) (i.e. we no longer need to compare class names), you can:

  • list the (linear) hierarchy for each class
  • compare result lists

I just realized that comparing class names was not a requirement in your question.

Take an example:

 class C {} class B {} class A extends C {} class D extends A {} 

List the hierarchy of each class:

  • C → (C)
  • B → (B)
  • A → (C, A)
  • D → (C, A, D)

Then you get the full order:

  • B → (B)
  • C → (C)
  • A → (C, A)
  • D → (C, A, D)
+1
source

I think you should 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()); } } 
+1
source

It's a bit late to post ( Orest Savchak posted a very similar, but not quite the same answer before), but you can find the closest common ancestor and then calculate the distance of each class to this object. All classes (except classes for primitives) have a common ancestor (in the worst case, Object class), and we can use this information to provide some order: in the worst case, you can simply compare the two classes according to their "depth" in their respective inheritance hierarchies embedded from Object .

In addition, if one compared class is found when navigating the class hierarchy, then the other class is considered a child and, therefore, is "larger" than the other:

 public final class InheritanceDepthComparator<T> implements Comparator<Class<? extends T>> { private static class B {}; private static class C {}; private static class A extends C {}; private static <SUP, SUB extends SUP> int calculateInheritanceDistance(final Class<? extends SUB> o1, final Class<? extends SUP> o2) { int result = 0; Class<?> o1Parent = o1; do { o1Parent = o1Parent.getSuperclass(); result++; } while (!Objects.equals(o1Parent, o2)); return result; } @Override public int compare(final Class<? extends T> o1, final Class<? extends T> o2) { int result = 0; if (!o1.equals(o2)) { // Walk up the inheritance hierarchy int o1Depth = -1; Class<?> o1Parent = o1; do { o1Parent = o1Parent.getSuperclass(); o1Depth++; if (o1Parent.equals(o2)) { // The first compared object is a child of the second and // therefore "greater" than it result = 1; break; } else if (o1Parent.isAssignableFrom(o2)) { // Found the common ancestor class; At least by reaching // "Object", this should always be executed anyway // TODO: Check performance and see if manually going up the tree for o2 within this while loop itself is not faster final int o2Depth = calculateInheritanceDistance(o2, o1Parent); result = Integer.compare(o1Depth, o2Depth); break; } } while (o1Parent != null); } return result; } public static void main(final String[] args) { final InheritanceDepthComparator<Object> cComp = new InheritanceDepthComparator<>(); System.out.println("Parent compared to child: " + cComp.compare(C.class, A.class)); System.out.println("Child compared to parent: " + cComp.compare(A.class, C.class)); System.out.println("C (child) Compared to Object (parent): " + cComp.compare(A.class, Object.class)); System.out.println("Sibling classes:" + cComp.compare(A.class, B.class)); } } 

Then you can use this Comparator in the chain to process the sibling classes:

 final Comparator<Class<?>> cmp = new InheritanceDepthComparator<>().thenComparing(Class::getName); 
0
source

So, we want to define the general order in the classes so that any parent class / interface is less than any derived class / interface.

Decision:

  • Any interface class is smaller than any object class.

  • to compare two interfaces, we compare the number of their super interfaces. If they are equal, we compare their names.

  • to compare two classes of objects, we compare the number of their superclasses. If they are equal, we compare their names.

Why is this correct. A derived class always has more ancestors than any of its superclasses. Therefore, if we compare the number of ancestors, we guarantee that superclasses go to their descendants. As for the ordering within a group of classes that have N parents, any order will be done, the alphabetical order is in order.

 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 (fs != 0) { return fs; } 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; } } 
0
source

Source: https://habr.com/ru/post/1014228/


All Articles