Java,静态方法绑定和泛型都被卷入了一些方法重载

前端之家收集整理的这篇文章主要介绍了Java,静态方法绑定和泛型都被卷入了一些方法重载前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
所以标题意味着我的问题有点奇怪和复杂.我知道我要做什么打破所有的“好”编程实践的规则,但是嘿,如果我们不生活有什么生活?

所以我做的是创建以下程序. (这不是一个更大的实验的一部分,真正尝试和理解泛型,所以一些功能名称可能有点不合格)

import java.util.*;

public class GenericTestsClean 
{
    public static void test2()
    {
        BigCage<Animal> animalCage=new BigCage<Animal>();
        BigCage<Dog> dogCage=new BigCage<Dog>();
        dogCage.add(new Dog());
        animalCage.add(new Cat());
        animalCage.add(new Dog());
        animalCage.printList(dogCage);
        animalCage.printList(animalCage);
    }


    public static void main(String [] args)
    {
        //What will this print
        System.out.println("\nTest 2");
        test2();
    }

}

class BigCage<T> extends Cage<T>
{

    public static <U extends Dog> void printList(List<U> list)
    {
        System.out.println("*************"+list.getClass().toString());
        for(Object obj : list)
            System.out.println("BigCage: "+obj.getClass().toString());
    }

}
class Cage<T> extends ArrayList<T>
{
    public static void printList(List<?> list)
    {
        System.out.println("*************"+list.getClass().toString());
        for(Object obj : list)
            System.out.println("Cage: "+obj.getClass().toString());
    }
}

class Animal
{
}
class Dog extends Animal
{
}
class Cat extends Animal
{
}

现在让我感到困惑的是,它使用javac 1.6.0_26进行编译,但是当我运行它时,会得到以下类转换异常:

Test 2
*************class BigCage
BigCage: class Dog
*************class BigCage
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Dog
        at BigCage.printList(GenericTestsClean.java:31)
        at GenericTestsClean.test2(GenericTestsClean.java:13)
        at GenericTestsClean.main(GenericTestsClean.java:21)

这里要注意的一些事情:

>两个printList不会被覆盖,但是按预期的方式重载(他们有不同的类型,因为它们的参数的泛型类型不同).这可以通过使用@Override注释进行验证
>将Cage类中的void printList(List<?>)方法更改为非静态会生成适当的编译时错误
>更改方法void< U扩展Dog>类BigCage中的printList(List< U>)为void< U> printList(List< U>)生成适当的错误.
>在main()中通过BigCage类(即BigCage.printList(…))调用printList())生成相同的运行时错误
>在main()中通过Cage(即Cage.printList(…))调用printList()的工作原理只有在Cage中调用printList的版本
>如果我将printList(List<?>)的定义从Cage类复制到BigCage类,这将隐藏Cage类中的定义,我得到了相应的编译器错误

现在如果我不得不在黑暗中拍摄一下这里发生了什么,我会说编译器正在拧紧,因为它在多个阶段工作:类型检查和重载方法解析.在类型检查期间,我们通过违规行,因为BigCage类继承了Cage类中的void printList(List<?>),它将匹配我们抛出的任何旧列表,所以确保我们有一个可以工作的方法.但是,一旦遇到实际调用方法解决问题,我们就会遇到类型擦除问题,这会导致BigCage.printList和Cage.printList都具有完全相同的签名.这意味着当编译器正在寻找一个匹配的animalCage.printList(animalCage);它将选择它匹配的第一种方法(如果我们假设它从底部开始使用BigCage,并且将它的原因作为Object),它会找到void< U extends Dog> printList(List< U>)而不是正确的匹配void printList(List<?>)

现在我真正的问题:我在这里接近真相?这是一个已知的错误吗?这是一个bug吗?我知道如何解决这个问题,这更像是一个学术问题.

**EDIT**

As few people have posted below,this code will work in Eclipse.
My specific question deals with javac version 1.6.0_26. Also,I’m not
sure if I completely agree with Eclipse in this case,even though it
works,because adding a printList(List<?>) to BigCage will
result in a compile time error in Eclipse and I can’t see reason why
it should work when the same method is inherited verses manually
added (See Note 6 above).

解决方法

考虑这个微不足道的问题:
class A
{
    static void foo(){ }
}
class B extends A
{
    static void foo(){ }
}
void test()
{
    A.foo();
    B.foo();
}

假设我们从B中删除了foo方法,我们只重新编译B本身,当我们运行test()时会发生什么?是否会发现链接错误,因为没有找到B.foo()?

根据JLS3#13.4.12,删除B.foo不会破坏二进制兼容性,因为A.foo仍然被定义.这意味着,当执行B.foo()时,调用A.foo().记住,没有重新编译test(),所以这个转发必须由JVM处理.

相反,我们从B中删除foo方法,然后重新编译全部.即使编译器静态地知道B.foo()实际上意味着A.foo(),它仍然在字节码中生成B.foo().现在,JVM将B.foo()转发到A.foo().但是,如果将来B获得新的foo方法,即使没有重新编译test(),新的方法将在运行时被调用.

在这个意义上,静态方法之间有一个压倒一切的关系.当编译看到B.foo()时,它必须将它编译为B.foo()in by代码,无论B是否有一个foo()今天.

在您的示例中,当编译器看到BigCage.printList(animalCage)时,它正确地推断出它实际上是调用Cage.printList(List<?>).因此,需要将该调用编译为BigCage.printList(List<?>)的字节码 – 目标类必须是BigCage而不是Cage.

哎呀!字节码格式尚未升级以处理方法签名.泛型信息以字节码保存为辅助信息,但是对于方法调用,它是旧的方式.

擦除发生.该调用实际上编译成BigCage.printList(List).太糟糕了,BigCage在擦除后也有一个printList(List).在运行时,该方法调用

这个问题是由于Java规范和JVM规范之间的不匹配造成的.

Java 7收紧了一点;实现字节码和JVM无法处理这种情况,它不再编译您的代码

error: name clash:
printList(List) in BigCage and
printList(List) in Cage have the
same erasure,yet neither hides the
other

另一个有趣的事实:如果两种方法有不同的返回类型,你的程序将正常工作.这是因为在字节码中,方法签名包括返回类型.所以Dog printList(List)和Object printList(List)之间没有混淆.另请参见Type Erasure and Overloading in Java: Why does this work?这个技巧只允许在Java 6中使用.Java 7禁止它,可能是由于技术之外的原因.

原文链接:https://www.f2er.com/java/121416.html

猜你在找的Java相关文章