2009-11-25 2 views
2

Сегодня я столкнулся с чем-то особенным. Посмотрите на этот фрагмент кода:Generics in Java

List <Rectangle> test1 = new LinkedList<Rectangle>(); 
List <Shape> test2 = test1; //Compiler Error; 

Это, конечно, при условии, что класс Rectangle подкласс Shape. Может кто-нибудь объяснить мне, почему это ошибка?

ответ

4

Если этот код работал, вы можете продолжить, включив в test2Circle - таким образом, совершенно нарушающего гарантию того, что test1 делает, что только Rectangle s никогда не будет вставлен в него.

Общий принцип (независимый от языка - вопрос логики - хотя и контр-интуитивный): мешок с бананами - это не мешок с фруктами ... в мире изменчивых объектов (мир функционального программирования, где каждый объект неизменен после создания, MUCH проще!). Это потому, что вы можете добавить яблоко в мешок с фруктами (так как яблоко - это кусок фруктов), но вы не можете добавить яблоко в сумку с бананами (так как яблоко не банан).

Кстати, это очень похоже на причину, по которой квадрат не является прямоугольником (опять же, в мире изменяемых объектов): потому что, учитывая (изменяемый) прямоугольник, вы можете мутировать обе стороны независимо, но, учитывая квадрат , вы не можете. (В геометрии квадрат IS действительно является прямоугольником - но это потому, что в геометрии, как в функциональном программировании, нет понятия «мутирования» объекта! -).

3

Это общий источник путаницы - дженерики не ковариантны в Java.

Для действительно хорошего объяснения этого смотрите Java theory and practice: Generics gotchas:

В то время как вы могли бы найти полезный думать коллекции, как будучи абстракции массивов, они имеют некоторые специальных свойства, коллекции делают не. Массивы на языке Java: covariant - это означает, что если Integer расширяет число (это ), то это не только целое число , но и число, но целое число [] равно также номер [] и вы можете использовать передать или назначить целое число [], где вызывается Номер []. (Подробнее формально, если Number является супертипом Целое число, то Number [] является супертипом Integer [].) Вы можете подумать, что тоже относится к общим типам - этот список является супертипом Список, и вы можете передать список , где ожидается . К сожалению, это не так .

5

Вам необходимо использовать дикую карту. Как это:

List<Rectangle> test1 = new LinkedList<Rectangle>(); 
List<? extends Shape> test2 = test1; 

Как Rectangle расширяет Shape.

Причина в том, что если test2 равен List<Shape>, вы можете ожидать, что test2.add (..) примет любую форму, но это не так, если вы разрешаете test1 (то есть List<Rectangle>), test1 не принимает любая форма, которая не является Rectangle.

Надеюсь, это поможет.

0

Список < Shape> не имеет отношения к списку < Rectangle>. Когда вы используете массив, Shape [] на самом деле является супертипом Rectangle [], потому что массивы являются ковариантными. Но коллекции, такие как List, не ковариантны. На самом деле во время выполнения, они оба будут стерты в виде списка < Object>, а при вызове любых методов из любых экземпляров списка < Shape>, Список < Rectangle>, ВМ будет вызывать методы Списка < Object> но с другой код, вставленный для проверки безопасности типа.