Bob*_*630 9 java generics overloading
因此,标题暗示我的问题有点奇怪和复杂.我知道我要做的事情打破了"好"编程实践的所有规则,但是嘿,如果我们不活一点,生活会怎样?
所以我做的是创建以下程序.(这不是一个更大的实验的一部分,真正尝试和理解泛型,所以一些功能名称可能有点乱序)
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
{
}
Run Code Online (Sandbox Code Playgroud)
现在令我困惑的是,这与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)
Run Code Online (Sandbox Code Playgroud)
这里需要注意的一些事项:
void printList(List<?>)方法更改为非静态会生成适当的编译时错误void <U extends Dog> printList(List<U>)在类BigCage到void <U> printList(List<U>)产生适当的错误.printList(List<?>),以类BigCage从类笼,这将隐藏在定义类凯奇,我得到相应的编译器错误现在,如果我不得不在黑暗中拍摄这里发生的事情,我会说编译器搞砸了,因为它在多个阶段工作:类型检查和重载方法解析.在类型检查阶段,我们通过违规获得线因为类BigCage继承void printList(List<?>)自class Cage这将匹配任何旧列表,我们在它扔,所以相信我们有,将工作的方法.然而,一旦到了解决实际调用方法的时候,由于类型擦除导致两者BigCage.printList并且Cage.printList具有完全相同的签名,我们就会遇到问题.这意味着当编译器正在寻找匹配时,animalCage.printList(animalCage);它将选择它匹配的第一个方法(如果我们假设它从BigCage的底部开始,并将其工作原理直到Object)它将void <U extends Dog> printList(List<U>)首先找到而不是正确的匹配void printList(List<?>)
现在我的真实问题是:我在这里的真相有多接近?这是一个已知的错误?这是一个错误吗?我知道如何解决这个问题,这更像是一个学术问题.
**编辑**
由于下面发布的人很少,这段代码将在Eclipse中运行.我的具体问题涉及javac版本1.6.0_26.此外,我不确定在这种情况下我是否完全同意Eclipse,即使它有效,因为
printList(List<?>)在BigCage中添加一个将导致Eclipse中的编译时错误,我无法理解为什么它应该在相同时工作方法是手工添加的继承(参见上面的注释6).
考虑一下这个微不足道的问题:
class A
{
static void foo(){ }
}
class B extends A
{
static void foo(){ }
}
void test()
{
A.foo();
B.foo();
}
Run Code Online (Sandbox Code Playgroud)
假设我们foo从中删除了方法B,并且我们只重新编译B自己,运行时会发生什么test()?它应该抛出链接错误,因为B.foo()没有找到?
根据JLS3#13.4.12,删除B.foo不会破坏二进制兼容性,因为A.foo仍然定义.这意味着,在B.foo()执行时,A.foo()将调用它.请记住,没有重新编译test(),所以这个转发必须由JVM处理.
相反,让我们foo从中删除方法B,并重新编译所有方法.即使编译器静态地知道B.foo()实际意味着A.foo()它,它仍然B.foo()在字节码中生成.目前,JVM将转发B.foo()给A.foo().但是如果将来B获得一个新foo方法,新方法将在运行时调用,即使test()没有重新编译.
从这个意义上说,静态方法之间存在着一种压倒一切的关系.当编译看到时B.foo(),它必须将其编译为B.foo()字节码,无论是否B有foo()今天.
在您的示例中,当编译器看到时BigCage.printList(animalCage),它正确地推断它实际上正在调用Cage.printList(List<?>).所以它需要将调用编译为字节码BigCage.printList(List<?>)- 目标类必须在BigCage这里而不是Cage.
哎呀!字节码格式尚未升级为处理方法签名.泛型信息在字节码中保留为辅助信息,但对于方法调用,它是旧的方式.
擦除发生了.该调用实际上已编译成BigCage.printList(List).太糟糕了BigCage还有一个printList(List)擦除后.在运行时,调用该方法!
此问题是由于Java规范和JVM规范之间的不匹配造成的.
Java 7收紧了一点; 实现字节码和JVM无法处理这种情况,它不再编译你的代码:
错误:名称冲突:BigCage中的printList(List)和Cage中的printList(List)具有相同的擦除,但都没有隐藏其他
另一个有趣的事实:如果这两种方法具有不同的返回类型,那么您的程序将正常工作.这是因为在字节码中,方法签名包括返回类型.因此,Dog printList(List)和之间没有混淆Object printList(List).另请参见Java中的类型擦除和重载:为什么这样做?这个技巧只允许在Java 6中使用.Java 7禁止它,可能是出于技术原因以外的原因.
| 归档时间: |
|
| 查看次数: |
2519 次 |
| 最近记录: |