document.querySelector('.nav-link');
Пожалуйста, попробуйте этот код.
"Добираются, и Помещенный Принцип" в разделе (2.4) является реальным драгоценным камнем от Дженериков Java и Наборов:
Получение и Помещенный Принцип: используйте, расширяет подстановочный знак, когда Вы только вытаскиваете значения из структуры, используйте супер подстановочный знак, когда Вы только помещаете значения в структуру и не используете подстановочный знак, когда Вы оба получаете и помещаете.
Кроме того, объявляя тип как List<? super Shape> shapeSuper
вид невоспитанности, поскольку это ограничивает свое использование. Обычно единственное время, я использую подстановочные знаки, находится в сигнатурах методов:
public void foo(List<? super Shape> shapeSuper)
Попытайтесь объявить shapeSuper как List<Shape>
вместо этого. Затем можно сделать
for (Shape shape : shapeSuper)
Подробно остановиться на ответе Paul, путем объявления shapeSuper как Список <? супер Форма>, Вы говорите, что она может принять любой объект, который является суперклассом Формы. Объект является суперклассом формы. Это означает, что общий суперкласс каждого из элементов списка является Объектом.
Поэтому необходимо использовать Тип объекта в для цикла. Что касается компилятора, список мог бы содержать объекты, которые не являются Формами.
Для Вашего примера можно использовать плоскость List<Shape>
как Dan и сказанный Paul; Вы не должны использовать подстановочный синтаксис вопросительного знака такой как List<? super Shape>
или List<? extends Shape>
). Я думаю, что Ваш базовый вопрос может быть, "когда я использовал бы одно из объявлений стиля вопросительного знака?" (Получение и Помещенный Принцип, что Julien цитирует, являются большим ответом на этот вопрос, но я не думаю, что это имеет много смысла, если Вы не видите его в контексте примера.) Вот мое взятие в расширенной версии Получения и Помещенного Принципа для того, когда использовать подстановочные знаки.
Использовать <? extends T>
если...
Foo<T>
readSourceИспользовать <? super T>
если...
Foo<T>
writeDestВот пошаговая демонстрация определенного примера, который иллюстрирует взгляды позади подстановочных знаков. Предположите запись 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;
}
}
(Правовая оговорка: я никогда не использовал "супер" в качестве универсального подстановочного спецификатора, поэтому возьмите это с мелкой частицей соли...),
Для (A) на самом деле Вы не можете добавить Форму и ее производные, можно только добавить Форму и ее предков. Я думаю, возможно, что Вы хотите,
List<? extends Shape> shapeSuper = new ArrayList<Shape>();
Определение "расширяется", означает Форму, и что-либо произошло из Формы. Определение "супер" означает Форму и что-либо, от чего Форма убывала.
Не уверенный в (B), если Объект не неявен. Что происходит, если Вы явно объявляете Форму как public class Shape extends Object
?
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());
операция.
Помните, Дженерики не являются ковариантными.
В отношении вышеупомянутого я не думаю, что это корректно:
путем объявления 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>());
}