Java类的比较器?

时间:2017-01-19 15:06:27

标签: java

我想比较两个Java类。

class ClassComparator implements Comparator<Class> {
    @Override
    public int compare(Class arg0, Class arg1) {
        return ....;
    }
}

我可以只比较类名,但我希望父类比从它们派生的类“更小”。我希望这个 less-than 关系是可传递的,适用于任何两个类。 (老实说,在我真正的问题中,一个类总是另一个类的超类,但我想要一些通用的代码,因为理论上这可以改变。)

也许这已经完成,有人可以共享代码段吗?

我想到的是:如果没有一个类派生自另一个类,找到它们从共同祖先派生的两个超类,并比较它们的名字。 (嗯,它甚至可以支持接口,如果任何对象类比任何接口都大。)

5 个答案:

答案 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 {}

然后你有:

  • A&lt;乙
  • B&lt; ç
  • C&lt; A

编辑:由于Comparator强加了总排序,因此您的问题无法解决。

编辑2:但是,如果您的约束中没有解决方案,则可以更改它们。如果你的目标是在类之间定义一个总顺序,那么超类总是小于一个子类(忽略接口)(即我们不再需要比较类名),你可以:

  1. 列出每个班级的(线性)层次结构
  2. 比较结果列表
  3. 我刚才意识到在你的问题中不需要比较类名。

    让我们举个例子:

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

    列出每个类的层次结构:

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

    然后,您得到一个总订单:

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

答案 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)

因此,我们希望在类上定义一个总顺序,使得任何父类/接口都比任何派生类/接口“更小”。

解决方案是:

  1. 任何接口类都小于任何对象类。

  2. 比较两个接口,我们比较它们的超接口数。如果他们是平等的,我们会比较他们的名字。

  3. 比较两个对象类,我们比较它们的超类数。如果他们是平等的,我们会比较他们的名字。

  4. 为什么这是正确的。派生类总是拥有比其任何超类更多的祖先。因此,如果我们比较祖先的数量,我们保证超类在他们的后代之前。至于具有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);