【发布时间】:2011-10-02 09:16:53
【问题描述】:
所以正如标题所暗示的那样,我的问题有点奇怪和复杂。我知道我将要做的事情打破了“良好”编程实践的所有规则,但是嘿,如果我们不活得一点点,生活会怎样?
所以我所做的是创建以下程序。 (请注意,这是真正尝试理解泛型的大型实验的一部分,因此某些函数名称可能有点乱序)
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 注释来验证
- 将 class Cage 中的
void printList(List<?>)方法更改为非静态会产生相应的编译时错误 - 将 class BigCage 中的方法
void <U extends Dog> printList(List<U>)更改为void <U> printList(List<U>)会产生相应的错误。 - 在 main() 中通过 class BigCage 调用 printList()(即 BigCage.printList(...))生成相同的运行时错误
- 在 main() 中,通过 class Cage 调用 printList()(即 Cage.printList(...))按预期工作只调用Cage中的printList版本
- 如果我将
printList(List<?>)的定义从 class Cage 复制到 class BigCage,这将隐藏 class Cage 中的定义,我得到相应的编译器错误
现在,如果我不得不在黑暗中试一试这里发生了什么,我会说编译器搞砸了,因为它在多个阶段工作:类型检查和重载方法解析。在类型检查阶段,我们通过了违规行,因为 class BigCage 从 class Cage 继承了 void printList(List<?>),这将匹配我们扔给它的任何旧 List,因此请确保我们有一个可以工作的方法。但是,一旦需要使用实际调用的方法来解决问题,我们就会因为类型擦除而出现问题,这会导致 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)。
【问题讨论】:
-
IANALL,但上面的注释 3 似乎是一把确凿的证据 - 它看起来像一个错误。另一方面,这些东西总是很乱——C++模板也有同样的问题——只是更多。
-
我在 Eclipse 中复制了相同的代码并运行了它。它工作得很好。没有例外。我已经复制了同一个文件中的所有类(我知道这是一种不好的做法,但我只是在检查你的问题)。
-
@Logan 我已经编辑了原始问题,以更好地反映您对 Eclipse 与 javac 的观点
标签: java generics overloading