Java、静态方法绑定和泛型都有一些方法重载

Java、静态方法绑定和泛型都有一些方法重载,java,generics,overloading,Java,Generics,Overloading,因此,正如标题所暗示的,我的问题有点奇怪和复杂。我知道我将要做的事情打破了“好”编程实践的所有规则,但是嘿,如果我们不活一点,生活会是什么呢 所以我做的是创建以下程序。(请注意,这是一个更大实验的一部分,该实验旨在真正尝试和理解泛型,因此一些函数名可能有点混乱) 这里需要注意以下几点: 这两个printList不是重写,而是像预期的那样彼此重载(它们具有不同的类型,因为它们的参数的泛型类型不同)。这可以通过使用@Override注释进行验证 将类框架中的void printList(List)方

因此,正如标题所暗示的,我的问题有点奇怪和复杂。我知道我将要做的事情打破了“好”编程实践的所有规则,但是嘿,如果我们不活一点,生活会是什么呢

所以我做的是创建以下程序。(请注意,这是一个更大实验的一部分,该实验旨在真正尝试和理解泛型,因此一些函数名可能有点混乱)

这里需要注意以下几点:

  • 这两个printList不是重写,而是像预期的那样彼此重载(它们具有不同的类型,因为它们的参数的泛型类型不同)。这可以通过使用@Override注释进行验证
  • 将类框架中的
    void printList(List)
    方法更改为非静态会生成相应的编译时错误
  • 类BigCage中的方法
    void printList(List)
    更改为
    void printList(List)
    会产生相应的错误
  • main()中,通过类BigCage调用printList()(即BigCage.printList(…)会生成相同的运行时错误
  • main()中,通过类框架调用printList()(即Cage.printList(…)按预期工作,仅在框架中调用printList的版本
  • 如果我将
    printList(List)
    的定义从class-Cage复制到class-BigCage,这将把定义隐藏在class-Cage中,我会得到相应的编译器错误
  • 现在,如果我必须在黑暗中尝试一下这里发生了什么,我会说编译器出了问题,因为它在多个阶段工作:类型检查和重载方法解析。在类型检查阶段,我们通过了有问题的行,因为类BigCage继承了
    类Cage
    中的
    无效打印列表(List)
    ,它将匹配我们抛出的任何旧列表,所以确保我们有一个有效的方法。然而,一旦到了使用方法实际调用解决问题的时候,我们就会遇到一个问题,原因是类型擦除导致
    BigCage.printList
    Cage.printList
    具有完全相同的签名。这意味着当编译器正在为
    animalage.printList(animalage)寻找匹配项时它将选择它匹配的第一个方法(如果我们假设它从底部的BigCage开始,并将其why up to Object)它将首先找到
    void printList(List)
    而不是正确的匹配
    void printList(List)

    现在我真正的问题是:我离真相有多近?这是已知的bug吗?这是一个错误吗?我知道如何避开这个问题,这更像是一个学术问题

    **编辑**

    由于很少有人在下面发帖,这段代码将在Eclipse中工作。 我的具体问题涉及javac版本1.6.026。而且,我不是 当然,如果我完全同意Eclipse在这种情况下,即使它 有效,因为将
    printList(List)
    添加到BigCage将 在Eclipse中导致编译时错误,我看不出原因 当手动继承相同的方法时,它应该工作 增加(见上文注释6)


    这不是一个bug。该方法是静态的。不能重写静态方法,只能隐藏它们


    在bigCage上调用“
    printList
    ”时,实际上是在bigCage类上调用
    printList
    ,而不是对象,对象将始终调用在bigCage类中声明的静态方法。

    IMHO此代码可能不正确类BigCage中的方法printList应导致名称冲突,因为Cage中的printList具有相同的擦除,但两者都不重写另一个。奇怪的是编译器编译它:)

    由此产生的字节码(javac1.6.0_10)相当于:

    class BigCage extends Cage {
    
        public static void printList(List list){
            System.out.println((new StringBuilder()).append("*************").append(list.getClass().toString()).toString());
            Dog dog;
            for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println((new StringBuilder()).append("BigCage: ").append(dog.getClass().toString()).toString()))
                dog = (Dog)iterator.next();
        }
    }
    
    强制转换循环导致异常。 Eclipse内置编译器生成这样的代码(正常工作):

    或者源代码还可以,但编译器正在创建错误的字节码?
    事实上,我们使用参数
    BigCage animalCage
    调用方法
    void printList(List List)
    ,而动物不扩展狗。

    这是该代码的最简单版本,具有相同的问题:

    import java.util.*;
    
    public class GenericTestsClean {
        public static void main(String[] args) {
            List<Animal> animalCage = new ArrayList<Animal>();
            animalCage.add(new Cat());
            animalCage.add(new Dog());
            BigCage.printList(animalCage);
        }
    }
    
    class Animal {}
    class Dog extends Animal {}
    class Cat extends Animal {}
    
    class BigCage extends Cage {
        public static <U extends Dog> void printList(List<U> list) {
            System.out.println("BigCage#printList");
            for (Object obj : list) {
                System.out.println("BigCage: " + obj.getClass().toString());
            }
        }
    }
    
    class Cage {
        public static void printList(List list) {
            System.out.println("Cage#printList");
            for (Object obj : list) {
                System.out.println("Cage: " + obj.getClass().toString());
            }
        }
    }
    
    调用java GenericTestsClean

    javac1.6.0\u10版本

    BigCage#printList
    Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Dog
            at BigCage.printList(GenericTestsClean.java:19)
            at GenericTestsClean.main(GenericTestsClean.java:8)
    
    BigCage#printList
    BigCage: class Cat
    BigCage: class Dog
    
    Eclipse编译器版本

    BigCage#printList
    Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Dog
            at BigCage.printList(GenericTestsClean.java:19)
            at GenericTestsClean.main(GenericTestsClean.java:8)
    
    BigCage#printList
    BigCage: class Cat
    BigCage: class Dog
    

    我认为这两个结果都不正确。

    考虑一下这个小问题:

    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()
    ,新方法也会在运行时被调用

    从这个意义上讲,静态方法之间存在一种覆盖关系。当compile看到
    B.foo()
    时,它必须在bytec中将其编译为
    B.foo()
    BigCage#printList
    Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Dog
            at BigCage.printList(GenericTestsClean.java:19)
            at GenericTestsClean.main(GenericTestsClean.java:8)
    
    BigCage#printList
    BigCage: class Cat
    BigCage: class Dog
    
    class A
    {
        static void foo(){ }
    }
    class B extends A
    {
        static void foo(){ }
    }
    void test()
    {
        A.foo();
        B.foo();
    }