Так что, как следует из названия, мой вопрос немного странный и сложный.Я знаю, что я собираюсь сделать, нарушает все правила «хороших» практик программирования, но, эй, что за жизнь, если мы немного не проживем?
Итак, что я сделал, так это создал следующую программу.(Не это было частью большого эксперимента, чтобы по-настоящему попытаться понять дженерики, поэтому некоторые имена функций могут быть немного не в порядке)
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
- Изменение метода
void printList(List<?>)
в классе Cage на нестатическое приводит к соответствующей ошибке времени компиляции - Изменениеметод
void <U extends Dog> printList(List<U>)
в классе BigCage до void <U> printList(List<U>)
генерирует соответствующую ошибку. - В main () вызывает printList () через класс BigCage (т.е. BigCage.printList (...)) генерирует ту же ошибку времени выполнения
- В main () , вызывая printList () через класс Cage (т.е. Cage.printList (...)) работает как положено, вызывая только версию printList в Cage
- Если я скопирую определение для
printList(List<?>)
в класс BigCage из класс Cage , который скроет определение в класс Cage , я получу соответствующую ошибку компилятора
Теперь, если бы мне пришлось сделать снимок в темноте относительно того, что здесь происходит, я бы сказал, что компилятор испортит, потому чтоПо сути, он работает в несколько этапов: Проверка типа и Перегрузка Разрешение метода .На этапе проверки типа мы проходим через строку, вызывающую ошибку, потому что класс 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 выше).