Generics в Java

от автора

Сегодня хотел бы рассказать вам о generics в Java и его некоторых особенностях. Данная возможность появилась в Java с версии J2SE5 и с тех пор полностью изменила работу с Collections. С помощью данного подхода мы можем использовать различные структуры и контейнеры с разными типами данных, не изменяя их описания.

Впрочем, рассмотрим простой пример с Generics и без них для общего понимания.

(1)

List<String> words = new ArrayList<String>(); words.add("Hello "); words.add("world!"); String s = words.get(0)+words.get(1); System.out.println(s.equals("Hello world!")); 

Это пример, собственно, с Generis. Тут представлен список строк, в который мы добавляем две произвольные строки. В данном случае обобщенным типом является класс ArrayList (вместо этого T мы можем подставить любой тип, который не является примитивным типом).

Давайте рассмотрим код, который вероятно писали до Generics:

(2)

List words = new ArrayList(); words.add("Hello "); words.add("world!"); String s = ((String)words.get(0))+((String)words.get(1)) System.out.println(s.equals("Hello world!")); 

Как видим, в принципе разницы никакой нет, единственное, что требуется — выполнять явное преобразование с типа Object в тип String.

Однако, компилятору не важно каким способом вы будете пользоваться, оба примера будут одинаково представлены в байт-коде. По сути, Generic-версия изначально представиться в (2) а потом уже будет транслирована в байт-код. Это так называемый процесc erasure(стирание).

Преимущество (1) в том, что проверка на ошибки будет выполнена компилятором на этапе компиляции, в отличии от (2), которая не будет произведена.

В результате в (3) может возникнуть Runtime error:

Exception in thread «main» java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

(3)

List words = new ArrayList(); words.add("Hello "); words.add(1); String s = ((String)words.get(0))+((String)words.get(1)); 

Wildcards

Wildcards вместе с extends. Иногда в колекцию нам требуется сохранить не только определенно одни типы, а еще и их подтипы, для этих целей и используються WildCards (можно просто Маски).
Давайте рассмотрим пример:

interface Collection<E> { ... public boolean addAll(Collection<? extends E> c); ... } 

Метод addAll() позволяет добавлять элементы одной колекции в другую. Выражение “? extends E” означает, что мы можем добавлять подтипы типа Е в коллекцию.

List<Number> nums = new ArrayList<Number>(); List<Integer> ints = Arrays.asList(1, 2); List<Double> dbls = Arrays.asList(2.78, 3.14); nums.addAll(ints); nums.addAll(dbls); assert nums.toString().equals("[1, 2, 2.78, 3.14]"); 

В данном примере мы создали пустой список, в который добавили список элементов Integer и Double. Это действие не вызовет ошибок компиляции, так как ints имеет тип List, что является подтипом Collection<? extends Number>. То же самое с dbls.

WildCards вместе с super. Так же, как и c extends, WildCard с super имеет вид: “? super T “, пример c Generic методом:

public static <T> void copy(List<? super T> dst, List<? extends T> src) { for (int i = 0; i < src.size(); i++) { dst.set(i, src.get(i)); } } //------------------------------------------------------------------------- List<Object> objs = Arrays.<Object>asList(2, 3.14, "four"); List<Integer> ints = Arrays.asList(5, 6); Collections.copy(objs, ints); assert objs.toString().equals("[5, 6, four]"); 

“? super T “ означает, что dst список может вместить в себе элементы, которые являются супертипами типа Т.

The Get and Put principle. Вопрос: когда же нам использовать маски с super и маски с extends? Правило очень простое: используйте extends, когда нужно получить значения со структуры, и super, когда нужно поместить значания в структуру. Если же добавляем и извлекаем элементы, то вообще не используем wildcards.

По сути, использование данного принципа можно увидеть в предыдущем примере.

 public static <T> void copy(List<? super T> dest, List<? extends T> src) 

Итоги

— Generics позволяют уменьшить количество ошибок в преобразованиях типов в коде за счет проверки их во время компиляции;
— Делают код чище и удобно читаемым.

Литература: M. Naftalin, P. Wadler — Java Generics and Collections

ссылка на оригинал статьи http://habrahabr.ru/post/275149/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *