使用反射比较两个比较方法的比较器

5
我正在尝试修复一个未知比较器(无源代码访问)。因此,我编写了一些反射代码来查看该比较器接受哪些类型。令人惊讶的是,反射告诉我,有两个compare方法,一个是真实类型,一个是Object。
Comparator<Integer> comp = new Comparator<Integer>()
{

    @Override
    public int compare(Integer o1, Integer o2)
    {
        return 0;
    }
};

Method[] methods = comp.getClass().getMethods();
for (Method method : methods)
{
    if(method.getName().equals("compare")){
        System.out.println(method);
    }
}

输出:

public int de.hinneLinks.stackoverflow.MyClass$1.compare(java.lang.Integer,java.lang.Integer)
public int de.hinneLinks.stackoverflow.MyClass$1.compare(java.lang.Object,java.lang.Object)

第二个compare方法来自哪里?

但它无法使用,为什么呢?

comp.compare(1, 2); //Compiles
comp.compare((Object)1,(Object)2); //Does not Compile

然而,我可以使用反射调用这些方法。如果我使用 new Object() 调用这两个方法,那么我将会得到两个不同的异常:

compare(java.lang.Object,java.lang.Object)
java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer

compare(java.lang.Integer,java.lang.Integer)
java.lang.IllegalArgumentException: argument type mismatch

如果我用Object来定义我的比较器(Comparator), 那么只会有一个方法。
2个回答

7
另一种方法(compare(Object obj1, Object2)是编译器生成的桥接方法,用于在类型擦除后保留二进制兼容性:
编译扩展参数化类或实现参数化接口的类或接口时,编译器可能需要创建一个合成方法,称为桥接方法作为类型擦除过程的一部分。您通常不需要担心桥接方法,但如果它出现在堆栈跟踪中,您可能会感到困惑。
您可以在该方法上添加检查以查看它是否是桥接方法:
for (Method method : methods) {
    if (method.getName().equals("compare") && !method.isBridge()) {
        System.out.println(method);
    }
}

3
这是由于Java中泛型的实现决策(保留向后兼容性)而引起的类型擦除
在类型擦除过程中,Java编译器会擦除所有类型参数,并用其第一个边界替换每个参数,如果类型参数有边界,则使用其第一个边界,否则使用Object。
这使得您的类可以从Java 5之前的JVM中访问,其中Comparator<Integer>不可见,但Comparator可见(它提供了compare(Object, Object))。compare(Object, Object)的实现将每个参数强制转换为Integer并调用compare(Integer, Integer),这就是为什么您会收到异常的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接