HTML Diff
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&lt;? extends Number&gt;. В таком случае в метод попадут только числа, какими бы они не были, а типом параметра numbers станет List&lt;Number&gt;.</p>
5 <p>Для подобных задач в Java есть механизм<em>Wildcard</em>, с его помощью можно уточнить типы, с которыми работает дженерик. В нашем случае и Integer и Double являются подтипами Number, а значит мы можем написать так: List&lt;? extends Number&gt;. В таком случае в метод попадут только числа, какими бы они не были, а типом параметра numbers станет List&lt;Number&gt;.</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>, потому что с помощью &lt;? extends Number&gt; мы указываем, что тип, который может быть передан в метод, должен быть подтипом Number. Другими словами, мы задаем верхнюю границу для возможных типов, которые могут быть использованы. Это позволяет работать с любыми типами, которые являются наследниками Number (например, Integer, Double, Float и т.д.). Но при этом запрещает передачу типов, которые не входят в эту иерархию.</p>
7 <p>Этот подход называется<strong>ограничением сверху</strong>, потому что с помощью &lt;? extends Number&gt; мы указываем, что тип, который может быть передан в метод, должен быть подтипом 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&lt;Integer&gt;, но и с List&lt;Number&gt; или даже List&lt;Object&gt;.</p>
10 <p>Ограничение снизу применяется, когда мы хотим добавлять элементы в коллекцию, но не хотим ограничиваться конкретным типом. Например, если у нас есть метод, который добавляет числа в список, мы можем использовать super, чтобы этот метод мог работать не только с List&lt;Integer&gt;, но и с List&lt;Number&gt; или даже List&lt;Object&gt;.</p>
11 <p>Рассмотрим пример:</p>
11 <p>Рассмотрим пример:</p>
12 <p>Метод addNumbers() принимает список, который может содержать элементы типа Integer или его супертипы (например, Number или Object). Таким образом, мы ограничиваем тип снизу. Это позволяет добавлять целые числа в список, даже если он объявлен как List&lt;Number&gt; или List&lt;Object&gt;</p>
12 <p>Метод addNumbers() принимает список, который может содержать элементы типа Integer или его супертипы (например, Number или Object). Таким образом, мы ограничиваем тип снизу. Это позволяет добавлять целые числа в список, даже если он объявлен как List&lt;Number&gt; или List&lt;Object&gt;</p>
13 <p>Здесь мы передаем список List&lt;Number&gt; в метод addNumbers(), и он успешно добавляет в него целые числа</p>
13 <p>Здесь мы передаем список List&lt;Number&gt; в метод 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&lt;? extends Number&gt; подходит для чтения чисел из списка.</p>
16 <p>Если коллекция является источником данных (производителем, producer), используйте extends. Например, List&lt;? extends Number&gt; подходит для чтения чисел из списка.</p>
17 <p>Если вы записываете данные в коллекцию, то коллекция является приемником данных (потребителем, consumer). В этом случае используйте super. Например, List&lt;? super Integer&gt; подходит для добавления целых чисел в список</p>
17 <p>Если вы записываете данные в коллекцию, то коллекция является приемником данных (потребителем, consumer). В этом случае используйте super. Например, List&lt;? super Integer&gt; подходит для добавления целых чисел в список</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>