Дженерики..? Супер T [дубликат]

document.querySelector('.nav-link');

Пожалуйста, попробуйте этот код.

12
задан Community 23 May 2017 в 10:30
поделиться

7 ответов

"Добираются, и Помещенный Принцип" в разделе (2.4) является реальным драгоценным камнем от Дженериков Java и Наборов:

Получение и Помещенный Принцип: используйте, расширяет подстановочный знак, когда Вы только вытаскиваете значения из структуры, используйте супер подстановочный знак, когда Вы только помещаете значения в структуру и не используете подстановочный знак, когда Вы оба получаете и помещаете.

alt text

Кроме того, объявляя тип как List<? super Shape> shapeSuper вид невоспитанности, поскольку это ограничивает свое использование. Обычно единственное время, я использую подстановочные знаки, находится в сигнатурах методов:

public void foo(List<? super Shape> shapeSuper)
17
ответ дан 2 December 2019 в 02:52
поделиться

Попытайтесь объявить shapeSuper как List<Shape> вместо этого. Затем можно сделать

for (Shape shape : shapeSuper)
4
ответ дан 2 December 2019 в 02:52
поделиться

Подробно остановиться на ответе Paul, путем объявления shapeSuper как Список <? супер Форма>, Вы говорите, что она может принять любой объект, который является суперклассом Формы. Объект является суперклассом формы. Это означает, что общий суперкласс каждого из элементов списка является Объектом.

Поэтому необходимо использовать Тип объекта в для цикла. Что касается компилятора, список мог бы содержать объекты, которые не являются Формами.

23
ответ дан 2 December 2019 в 02:52
поделиться

Для Вашего примера можно использовать плоскость List<Shape> как Dan и сказанный Paul; Вы не должны использовать подстановочный синтаксис вопросительного знака такой как List<? super Shape> или List<? extends Shape>). Я думаю, что Ваш базовый вопрос может быть, "когда я использовал бы одно из объявлений стиля вопросительного знака?" (Получение и Помещенный Принцип, что Julien цитирует, являются большим ответом на этот вопрос, но я не думаю, что это имеет много смысла, если Вы не видите его в контексте примера.) Вот мое взятие в расширенной версии Получения и Помещенного Принципа для того, когда использовать подстановочные знаки.

Использовать <? extends T> если...

  • Метод имеет универсальный параметр класса Foo<T> readSource
  • Метод ПОЛУЧАЕТ экземпляры T от readSource и не заботится, принадлежит ли полученный фактический объект подклассу T.

Использовать <? super T> если...

  • Метод имеет универсальный параметр класса Foo<T> writeDest
  • Метод ПОМЕЩАЕТ экземпляры T в writeDest и не заботится, содержит ли writeDest также объекты, которые являются подклассами T.

Вот пошаговая демонстрация определенного примера, который иллюстрирует взгляды позади подстановочных знаков. Предположите запись processSquare метода, который удаляет квадрат из списка, обрабатывает его и хранит результат в выходном списке. Вот сигнатура метода:

void processSquare(List<Square> iSqua, List<Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

Теперь Вы создаете список DoubleSquares, которые расширяют Квадрат и пытаются обработать их:

List<DoubleSquare> dsqares = ... 
List<Square> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! dsquares is not List<Square>

Компилятор перестал работать с ошибкой потому что тип dsquares List<DoubleSquare> не соответствует типу первого параметра к processSquare, List<Square>. Возможно, DoubleSquare - Квадрат, но необходимо сказать компилятор это a List<DoubleSquare> - a List<Square> в целях Вашего processSquare метода. Используйте <? extends Square> подстановочный знак, чтобы сказать компилятор, что Ваш метод может взять Список любого подкласса Квадрата.

void processSquare(List<? extends Square> iSqua, List<Square> oSqua)

Затем Вы улучшаете приложение для обработки Кругов, а также Квадратов. Вы хотите агрегировать все свои обработанные формы в единственном списке, который включает и круги и квадраты, таким образом, Вы изменили тип обработанного списка от a List<Square> к a List<Shape>:

List<DoubleSquare> dsqares = ... 
List<Circle> circles = ... 
List<Shape> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! processed is not List<Square>

Компилятор перестал работать с новой ошибкой. Теперь тип обработанного списка List<Shape> не соответствует 2-му параметру к processSquare, List<Square>. Используйте <? super Square> подстановочный знак, чтобы сказать компилятор, что данным параметром может быть Список любого суперкласса Квадрата.

void processSquare(List<? extends Square> iSqua, 
                          List<? super Square> oSqua) 

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

package wild;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public abstract class Main {
  // In processing the square, 
  // I'll take for input  any type of List that can PRODUCE (read) squares.
  // I'll take for output any type of List that can ACCEPT (write) squares.
  static void processSquare(List<? extends Square> iSqua, List<? super Square> oSqua) 
  { Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }

  static void processCircle(List<? extends Circle> iCirc, List<? super Circle> oCirc) 
  { Circle c = iCirc.remove(0); c.doCircle(); oCirc.add(c); }

  public static void main(String[] args) {
    // Load some inputs
    List<Circle> circles = makeList(new Circle());
    List<DoubleSquare> dsqares = makeList(new DoubleSquare());

    // Collated storage for completed shapes
    List<Shape> processed = new ArrayList<Shape>();

    // Process the shapes
    processSquare(dsqares, processed);
    processCircle(circles, processed);

    // Do post-processing
    for (Shape s : processed)
      s.shapeDone();
  }

  static class Shape { void shapeDone() { System.out.println("Done with shape."); } }
  static class Square extends Shape { void doSquare() { System.out.println("Square!"); } }
  static class DoubleSquare extends Square {}
  static class Circle extends Shape { void doCircle() { System.out.println("Circle!"); } }

  static <T> List<T> makeList(T a) { 
    List<T> list = new LinkedList<T>(); list.add(a); return list; 
  }

}
26
ответ дан 2 December 2019 в 02:52
поделиться

(Правовая оговорка: я никогда не использовал "супер" в качестве универсального подстановочного спецификатора, поэтому возьмите это с мелкой частицей соли...),

Для (A) на самом деле Вы не можете добавить Форму и ее производные, можно только добавить Форму и ее предков. Я думаю, возможно, что Вы хотите,

List<? extends Shape> shapeSuper = new ArrayList<Shape>();

Определение "расширяется", означает Форму, и что-либо произошло из Формы. Определение "супер" означает Форму и что-либо, от чего Форма убывала.

Не уверенный в (B), если Объект не неявен. Что происходит, если Вы явно объявляете Форму как public class Shape extends Object?

2
ответ дан 2 December 2019 в 02:52
поделиться

A)

Поскольку super указывает на более низкий класс ограничения универсального элемента. Так, List<? super Shape> мог представить List<Shape> или List<Object>.

B)

Поскольку компилятор не знает что фактический тип List<? super Shape> .

Ваше добавление объекта с shapeSuper.add(new Object()); но компилятор только знает что универсальный тип List супер тип Shape но не знает точно ведьму один, это.

В Вашем примере, List<? super Shape> мог действительно быть List<ShapeBase> принуждение компилятора запретить shapeSuper.add(new Object()); операция.

Помните, Дженерики не являются ковариантными.

4
ответ дан 2 December 2019 в 02:52
поделиться

В отношении вышеупомянутого я не думаю, что это корректно:

путем объявления shapeSuper как List<? super Shape> shapeSuper , Вы говорите, что это может принять любой объект, который является суперклассом Формы

Это действительно кажется интуитивным на первый взгляд, но на самом деле я не думаю, что это - то, как это работает. Вы не можете вставить просто суперкласс Формы в суперформу shapeSuper., на самом деле ссылка на Список, который может быть ограничен содержанием детали (но неуказанный) супертип Формы.

Позволяет предполагают, что Форма реализует Viewable и Drawable. Таким образом, в этом случае ссылка суперформы может на самом деле указать на a List<Viewable> или a List<Drawable> (или действительно a List<Object>) - но мы не знаем который. Если это на самом деле a List<Viewable> Вы не должны мочь вставить a Drawable экземпляр в него - и компилятор будет препятствовать тому, чтобы Вы делали это.

Конструкция нижней границы все еще на самом деле очень полезна в создании универсальных более гибких классов. В следующем примере это позволяет нам передавать в addShapeToSet метод Набор, определенный к содержанию любого суперкласса Формы - и мы можем все еще вставить Форму в него:

public void addShapeToSet(Set<? super Shape> set) {
    set.add(new Shape());
}

public void testAddToSet() {
    //these all work fine, because Shape implements all of these:
    addShapeToSet(new HashSet<Viewable>());
    addShapeToSet(new HashSet<Drawable>());           
    addShapeToSet(new HashSet<Shape>());           
    addShapeToSet(new HashSet<Object>());
}
0
ответ дан 2 December 2019 в 02:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: