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