1 added
2 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Дженерики позволяют нам работать однообразно с любым типом данных, но иногда возникает задача создать дженерик для определенного набора типов.</p>
1
<p>Дженерики позволяют нам работать однообразно с любым типом данных, но иногда возникает задача создать дженерик для определенного набора типов.</p>
2
<h2>Ограничение сверху (extends)</h2>
2
<h2>Ограничение сверху (extends)</h2>
3
<p>Возьмем для примера задачу поиска среднего значения для списка чисел. Для Integer реализация метода выглядит так:</p>
3
<p>Возьмем для примера задачу поиска среднего значения для списка чисел. Для Integer реализация метода выглядит так:</p>
4
<p>Если сделать из метода average() дженерик, то он не сработает, так как тип T будет Object, для которого операция сложения не определена.</p>
4
<p>Если сделать из метода average() дженерик, то он не сработает, так как тип T будет Object, для которого операция сложения не определена.</p>
5
<p>Для подобных задач в Java есть механизм<em>Wildcard</em>, с его помощью можно уточнить типы, с которыми работает дженерик. В нашем случае и Integer и Double являются подтипами Number, а значит мы можем написать так: List<? extends Number>. В таком случае в метод попадут только числа, какими бы они не были, а типом параметра numbers станет List<Number>.</p>
5
<p>Для подобных задач в Java есть механизм<em>Wildcard</em>, с его помощью можно уточнить типы, с которыми работает дженерик. В нашем случае и Integer и Double являются подтипами Number, а значит мы можем написать так: List<? extends Number>. В таком случае в метод попадут только числа, какими бы они не были, а типом параметра numbers станет List<Number>.</p>
6
<p>Обновленный код почти работает. Он все еще выдает ошибку<em>The operator += is undefined for the argument type(s) double, Number</em>, так как во время сложения получается что тип переменной sum это Double, а тип переменной number - Number. Эта задача решается за счет метода doubleValue() определенного у Number, который любое число преобразует в Double. Рабочий код:</p>
6
<p>Обновленный код почти работает. Он все еще выдает ошибку<em>The operator += is undefined for the argument type(s) double, Number</em>, так как во время сложения получается что тип переменной sum это Double, а тип переменной number - Number. Эта задача решается за счет метода doubleValue() определенного у Number, который любое число преобразует в Double. Рабочий код:</p>
7
<p>Этот подход называется<strong>ограничением сверху</strong>, потому что с помощью <? extends Number> мы указываем, что тип, который может быть передан в метод, должен быть подтипом Number. Другими словами, мы задаем верхнюю границу для возможных типов, которые могут быть использованы. Это позволяет работать с любыми типами, которые являются наследниками Number (например, Integer, Double, Float и т.д.). Но при этом запрещает передачу типов, которые не входят в эту иерархию.</p>
7
<p>Этот подход называется<strong>ограничением сверху</strong>, потому что с помощью <? extends Number> мы указываем, что тип, который может быть передан в метод, должен быть подтипом Number. Другими словами, мы задаем верхнюю границу для возможных типов, которые могут быть использованы. Это позволяет работать с любыми типами, которые являются наследниками Number (например, Integer, Double, Float и т.д.). Но при этом запрещает передачу типов, которые не входят в эту иерархию.</p>
8
<h2>Ограничение снизу (super)</h2>
8
<h2>Ограничение снизу (super)</h2>
9
<p>Мы разобрали, как Wildcard с ограничением сверху позволяет работать с типами, которые являются подтипами определенного класса или интерфейса. Это особенно полезно, когда нам нужно читать данные из коллекции, сохраняя гибкость в выборе типов. Но что, если нам нужно не только читать, но и записывать данные в коллекцию? Здесь на помощь приходит противоположный подход -<strong>ограничение снизу</strong>.</p>
9
<p>Мы разобрали, как Wildcard с ограничением сверху позволяет работать с типами, которые являются подтипами определенного класса или интерфейса. Это особенно полезно, когда нам нужно читать данные из коллекции, сохраняя гибкость в выборе типов. Но что, если нам нужно не только читать, но и записывать данные в коллекцию? Здесь на помощь приходит противоположный подход -<strong>ограничение снизу</strong>.</p>
10
<p>Ограничение снизу применяется, когда мы хотим добавлять элементы в коллекцию, но не хотим ограничиваться конкретным типом. Например, если у нас есть метод, который добавляет числа в список, мы можем использовать super, чтобы этот метод мог работать не только с List<Integer>, но и с List<Number> или даже List<Object>.</p>
10
<p>Ограничение снизу применяется, когда мы хотим добавлять элементы в коллекцию, но не хотим ограничиваться конкретным типом. Например, если у нас есть метод, который добавляет числа в список, мы можем использовать super, чтобы этот метод мог работать не только с List<Integer>, но и с List<Number> или даже List<Object>.</p>
11
<p>Рассмотрим пример:</p>
11
<p>Рассмотрим пример:</p>
12
<p>Метод addNumbers() принимает список, который может содержать элементы типа Integer или его супертипы (например, Number или Object). Таким образом, мы ограничиваем тип снизу. Это позволяет добавлять целые числа в список, даже если он объявлен как List<Number> или List<Object></p>
12
<p>Метод addNumbers() принимает список, который может содержать элементы типа Integer или его супертипы (например, Number или Object). Таким образом, мы ограничиваем тип снизу. Это позволяет добавлять целые числа в список, даже если он объявлен как List<Number> или List<Object></p>
13
<p>Здесь мы передаем список List<Number> в метод addNumbers(), и он успешно добавляет в него целые числа</p>
13
<p>Здесь мы передаем список List<Number> в метод addNumbers(), и он успешно добавляет в него целые числа</p>
14
<h2>Принцип PECS</h2>
14
<h2>Принцип PECS</h2>
15
<p>Чтобы лучше понять, когда использовать extends а когда super, полезно запомнить принцип PECS (Producer Extends, Consumer Super). Этот принцип помогает определить, какой тип Wildcard следует использовать в зависимости от роли коллекции.</p>
15
<p>Чтобы лучше понять, когда использовать extends а когда super, полезно запомнить принцип PECS (Producer Extends, Consumer Super). Этот принцип помогает определить, какой тип Wildcard следует использовать в зависимости от роли коллекции.</p>
16
<p>Если коллекция является источником данных (производителем, producer), используйте extends. Например, List<? extends Number> подходит для чтения чисел из списка.</p>
16
<p>Если коллекция является источником данных (производителем, producer), используйте extends. Например, List<? extends Number> подходит для чтения чисел из списка.</p>
17
<p>Если вы записываете данные в коллекцию, то коллекция является приемником данных (потребителем, consumer). В этом случае используйте super. Например, List<? super Integer> подходит для добавления целых чисел в список</p>
17
<p>Если вы записываете данные в коллекцию, то коллекция является приемником данных (потребителем, consumer). В этом случае используйте super. Например, List<? super Integer> подходит для добавления целых чисел в список</p>
18
<p>Wildcard с ограничениями не часто используется в прикладном коде, но часто используется в библиотеках. Поэтому его нужно знать на начальном этапе, хотя бы для того, чтобы читать документацию и понимать что там написано.</p>
18
<p>Wildcard с ограничениями не часто используется в прикладном коде, но часто используется в библиотеках. Поэтому его нужно знать на начальном этапе, хотя бы для того, чтобы читать документацию и понимать что там написано.</p>
19
<p>Например, вот как выглядит определение метода Collections.copy()</p>
19
<p>Например, вот как выглядит определение метода Collections.copy()</p>
20
<p>Здесь dest - это список, который может содержать элементы типа T или его супертипы (потребитель), а src - это список, который может содержать элементы типа T или его подтипы (производитель).</p>
20
<p>Здесь dest - это список, который может содержать элементы типа T или его супертипы (потребитель), а src - это список, который может содержать элементы типа T или его подтипы (производитель).</p>
21
<h2>Выводы</h2>
21
<h2>Выводы</h2>
22
<ul><li>Wildcard с ограничениями extends и super делает Generics в Java более гибкими и безопасными.</li>
22
<ul><li>Wildcard с ограничениями extends и super делает Generics в Java более гибкими и безопасными.</li>
23
<li>Используя extends, мы можем работать с коллекциями, которые производят данные</li>
23
<li>Используя extends, мы можем работать с коллекциями, которые производят данные</li>
24
<li>C помощью super мы можем работать с коллекциями, которые потребляют данные</li>
24
<li>C помощью super мы можем работать с коллекциями, которые потребляют данные</li>
25
<li>Принцип PECS помогает запомнить, когда и какой тип ограничения использовать</li>
25
<li>Принцип PECS помогает запомнить, когда и какой тип ограничения использовать</li>
26
-
</ul><p>Для более глубокого понимания темы мы рекомендуем просмотреть видео лекцию, которая является дополнительным материалом к данному курсу:</p>
26
+
</ul><p>Для более глубокого понимания темы мы рекомендуем просмотреть дополнительные материалы к данному курсу.</p>
27
-
<p>!vimeo!(818921329)</p>