Универсальные подстановочные знаки: назначение объекта суперкласса в список объектов подкласса

Question_1

class A {}
class B extends A {}

A aObj = new A();
B bObj = new B();

Мы знаем, что ниже будет приведено исключение класса в время выполнения.

List<B> list_B = new ArrayList<B>();
list_B.add((B)aObj); //ClassCast exception

Но

List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<B>();
List<A> list_A = (ArrayList<A>)list_4__A_AND_SubClass_A;
list_A.add(aObj); // No exception here, Why?

Question_2

В методе, описанном ниже

void method(List<? extends A> list_4__A_AND_SubClass_A){
   //How do i find the passed list is of Type A or B ??
   // To decide List<A> list_A = (ArrayList<A>)list_4__A_AND_SubClass_A;
   //        OR List<B> list_B = (ArrayList<B>)list_4__A_AND_SubClass_A;
}

ОБНОВЛЕНИЕ

Q_1

Лучше я должен избегать ссылки на сборку, как показано ниже для модификации; Если мы не уверены в том, что прошедший список является типом A/B; для сохранения целостности в этом списке.

 List<A> list_A = (ArrayList<A>)list_4__A_AND_SubClass_A; 

Q_2

Я думаю, мы можем передать еще один аргумент от метода вызывающего метода к вызывающему методу, чтобы поддерживать целостность над списком при модификации при вызове метода.

void method(List<? extends A> list_4__A_AND_SubClass_A, Class<? extends A> obj){
     if (A.class.isAssignableFrom(obj)) {
         List<A> list_A = (List<A>)list_4__A_AND_SubClass_A;
            list_A.add(new A());
            list_A.add(new B());
     } else if (B.class.isAssignableFrom(obj)){
         List<B> list_B = (List<B>)list_4__A_AND_SubClass_A;
            //list_B.add(new A()); //only we can add B
            list_B.add(new B());
     }
}

Ответ 1

Эта строка:

List<A> list_A = (ArrayList<A>)list_4__A_AND_SubClass_A;

Выдает предупреждение "unchecked", потому что вы можете (и действительно) лить список, содержащий только Bs, в список, который может содержать Bs или As (помните, поскольку B является подклассом A, это IS A), поэтому следующая строка

list_A.add(aObj);

отлично, потому что вы не обратили внимание на предупреждение.

В общем случае с generics List< ? extends A> не всегда совпадает с List<A>.

Ответ на вопрос 2.
К сожалению, все дженерики проверяются во время компиляции, и во время выполнения не так много информации, поэтому во время выполнения вы имеете только список объектов и не можете эффективно проверить, какой именно список. Эта "функция" была компромиссом между обратной совместимостью и дженериками, когда они были представлены в Java 1.5.

Ответ 2

В идеале, отличное от List<B> до List<A> должно исключать исключение во время выполнения; к сожалению, Java не может решить, что во время выполнения. К счастью, во время компиляции вы получите предупреждение для этого актера.

Ответ 3

  • Вы находитесь здесь List<? extends A>, определяя тип подстановочного знака, который означает, что список принимает все, что есть A или производное от A. Поэтому он не будет генерировать исключение.

  • Вы не можете определить здесь конкретный внутренний тип списка. Вы должны передать это напрямую, т.е. ваша подпись метода должна быть следующей:

    метод void (список списков)