我想比较两个Java类。
class ClassComparator implements Comparator<Class> {
@Override
public int compare(Class arg0, Class arg1) {
return ....;
}
}
我可以只比较类名,但我希望父类比从它们派生的类“更小”。我希望这个 less-than 关系是可传递的,适用于任何两个类。 (老实说,在我真正的问题中,一个类总是另一个类的超类,但我想要一些通用的代码,因为理论上这可以改变。)
也许这已经完成,有人可以共享代码段吗?
我想到的是:如果没有一个类派生自另一个类,找到它们从共同祖先派生的两个超类,并比较它们的名字。 (嗯,它甚至可以支持接口,如果任何对象类比任何接口都大。)
答案 0 :(得分:7)
您还可以将不在一个层次结构中的类与它们的深度和远离Object类的类进行比较。
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());
}
答案 1 :(得分:1)
您的约束不会产生有序集。
class C {}
class B {}
class A extends C {}
然后你有:
编辑:由于Comparator强加了总排序,因此您的问题无法解决。
编辑2:但是,如果您的约束中没有解决方案,则可以更改它们。如果你的目标是在类之间定义一个总顺序,那么超类总是小于一个子类(忽略接口)(即我们不再需要比较类名),你可以:
我刚才意识到在你的问题中不需要比较类名。
让我们举个例子:
class C {}
class B {}
class A extends C {}
class D extends A {}
列出每个类的层次结构:
然后,您得到一个总订单:
答案 2 :(得分:1)
我认为你应该能够做到:
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());
}
}
答案 3 :(得分:1)
因此,我们希望在类上定义一个总顺序,使得任何父类/接口都比任何派生类/接口“更小”。
解决方案是:
任何接口类都小于任何对象类。
比较两个接口,我们比较它们的超接口数。如果他们是平等的,我们会比较他们的名字。
比较两个对象类,我们比较它们的超类数。如果他们是平等的,我们会比较他们的名字。
为什么这是正确的。派生类总是拥有比其任何超类更多的祖先。因此,如果我们比较祖先的数量,我们保证超类在他们的后代之前。至于具有N个父类的类组中的排序,任何排序都可以,字母顺序是可以的。
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;
}
}
答案 4 :(得分:0)
发布稍晚(Orest Savchak posted a very similar-but-not-quite-the-same answer之前),但您可以找到最近的共同祖先,然后计算每个类与此对象的距离。 所有类(基元的类除外)都有一个共同的祖先(最糟糕的是,Object
类),我们可以使用这些信息以确保有一些排序:在最坏的情况下,您可以简单地在两个类的“深度”中比较它们各自的继承层次结构,这些层次结构来自Object
。
此外,如果在导航类层次结构时找到一个比较类,则另一个类被证明是一个子类,因此比另一个类“更大”:
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));
}
}
然后,您可以在链中使用此Comparator
来处理兄弟类:
final Comparator<Class<?>> cmp = new InheritanceDepthComparator<>().thenComparing(Class::getName);