Массив определенного класса для объектов коллекции? - PullRequest
2 голосов
/ 10 декабря 2011

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

У меня есть метод интерфейса, который выглядит как

private void render(Collection<Object> rows);

Теперь объекты, которые мне нужно передать, это массив (из перечисления):

Module[] mods = Module.values(); 
widget.render(mods);

Конечно, это не работает, но почему это не работает:

widget.render(Arrays.asList(mods))

Он превращает мой массив в коллекцию Module, а Module является объектом ...

Ответы [ 4 ]

6 голосов
/ 10 декабря 2011

Попробуйте изменить сигнатуру вашего метода на:

private void render(Collection<?> rows);

Это говорит о том, что ваш метод берет Collection с любым типом элемента, тогда как до того, как было сказано, Collection должен иметь конкретно Object в качестве параметра типа.

Использование такого подстановочного знака наложит ограничения на то, как вы можете использовать Collection, который передается в метод, особенно в отношении его изменения.Возможно, вы захотите показать нам, что делает ваш метод render, если вам нужен более подробный совет.

Этот пост стоит прочитать в отношении использования коллекций с подстановочными знаками в Java: Что такое PECS (Producer ExtendsПотребительский Супер)?

4 голосов
/ 10 декабря 2011

Так как Collection<Object> не является Collection<Module>.Хороший учебник о дженериках доступен в PDF-версии , и его обязательно нужно прочитать, когда вы работаете с дженериками.* часть.

0 голосов
/ 24 мая 2012

Причины этого основаны на том, как Java реализует дженерики.Лучший способ объяснить это - точное сравнение массивов и общих коллекций.

Пример массива

С массивами вы можете сделать это:

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

Но что произойдет, если вы попытаетесь это сделать?

Number[0] = 3.14; //attempt of heap pollution

Эта последняя строка будет прекрасно скомпилирована, но если вы запустите этот код, вы можете получить ArrayStoreException.

Это означает, что вы можете обмануть компилятор, но вы не можете обмануть систему типов времени выполнения.И это так, потому что массивы - это то, что мы называем типами reifiable .Это означает, что во время выполнения Java знает, что этот массив был фактически создан как массив целых чисел, к которым просто случается обращение через ссылку типа Number[].

Итак, как вы можете видеть, одна вещь - этоРеальный тип объекта, другая вещь - это тип ссылки, которую вы используете для доступа к ней, верно?

Проблема с обобщением Java

ТеперьПроблема с универсальными типами Java заключается в том, что информация о типах отбрасывается компилятором и недоступна во время выполнения.Этот процесс называется тип стирания .Есть веская причина для реализации таких обобщений в Java, но это длинная история, и она связана с бинарной совместимостью с уже существующим кодом.

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

Например,

List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);

List<Number> myNums = myInts;
myNums.add(3.14); //heap polution

Если компилятор Java не мешает вам делать это при компиляцииВ то же время система типов времени выполнения не может вас остановить, потому что во время выполнения невозможно определить, что этот список должен быть только списком целых чисел.Среда выполнения Java позволила бы вам поместить в этот список все, что вы хотите, когда он должен содержать только целые числа, потому что, когда он был создан, он был объявлен как список целых чисел.

Таким образом, разработчики Java сделалиуверен, что вы не можете обмануть компилятор.Если вы не можете обмануть компилятор (как мы можем это сделать с массивами), вы также не можете обмануть систему типов времени выполнения.

Таким образом, мы говорим, что универсальные типы non-reifiable .

Очевидно, это также будет препятствовать полиморфизму.Решение состоит в том, чтобы научиться использовать две мощные функции обобщений Java, известные как ковариация и контравариантность.

Ковариация

С помощью ковариации вы можете читать элементы из структуры, но выне могу ничего написать в него.Все это действительные декларации.

List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>()
List<? extends Number> myNums = new ArrayList<Double>()

И вы можете прочитать из myNums:

Number n = myNums.get(0);

Поскольку вы можете быть уверены, что, что бы ни содержал фактический список, его можно преобразовать вчисло (ведь все, что расширяет число, является числом, верно?)

Однако вам не разрешено помещать что-либо в ковариантную структуру.

myNumst.add(45L);

Это не разрешенопотому что Java не может гарантировать, что является реальным типом реального объекта.Это может быть что угодно, что расширяет Number, но компилятор не может быть уверен.Таким образом, вы можете читать, но не писать.

Контравариантность

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

List<Object> myObjs = new List<Object();
myObjs.add("Luke");
myObjs.add("Obi-wan");

List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);

В этом случае фактическая природа объекта - это Список объектов, и с помощью контравариантности вы можете поместить числав это, в основном, потому что числа имеют Объект в качестве общего предка.Таким образом, все числа являются объектами, и поэтому это действительно.

Однако вы не можете безопасно читать что-либо из этой контравариантной структуры, предполагая, что вы получите число.

Number myNum = myNums.get(0); //compiler-error

Как видите, если бы компилятор позволил вам написать эту строку, вы получите ClassCastException во время выполнения.

Принцип Get / Put

Таким образом, используйте ковариацию, когда вы намерены извлекать универсальные значения только из структуры, используйте контравариантность, когда вы только намереваетесь поместить универсальные значения в структуру, и используйте точный универсальный тип, когда вы собираетесь делать оба.

Лучший пример, который у меня есть, это следующее, которое копирует любые числа из одного списка в другой список.

public static void copy(List<? extends Number> source, List<? super Number> destiny) {
    for(Number number : source) {
        destiny.add(number);
    }
}

Благодаря силам ковариации и контравариантности это работает для случая, подобного этому:1087 *

List<Integer> myInts = asList(1,2,3,4);
List<Integer> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();

copy(myInts, myObjs);
copy(myDoubles, myObjs);
0 голосов
/ 10 декабря 2011

Если вы привели каждый объект в коллекции к модулю, например:

if(object instanceof Module)
{
    Module m = (Module)object;
    //Do stuff here with m
}

Тогда он должен работать.В противном случае два других ответа также работают нормально.

...