Сегодня я узнал..
Обобщения — это средства универсального программирования, которые были добавлены к языку программирования Java в 2004 году в рамках официальной версии J2SE 5.0. Они были разработаны для расширения системы типов Java, чтобы позволить «типу или методу работать с объектами различных типов, обеспечивая при этом безопасность типов во время компиляции. — Википедия
Скобки со стрелками, которые вы видите в Java, определяют дженерик. Обобщения выглядят так: ‹T› или ‹Integer› или что-то еще, что вы хотите поместить в него. Вы часто будете видеть, что он используется с такими типами данных, как ArrayList
или HashMap
. Например, ArrayList<Integer>
означает, что массив ограничен использованием только типов Integer или любых подклассов Integer.
Обобщения в Java — это способ рефакторинга кода путем абстрагирования объявлений типов. Вместо того, чтобы иметь много перегруженных методов, требующих определенного типа возврата, аргументов и т. д., вы можете преобразовать все распространенные методы в один метод, который принимает универсальный тип, представляющий любой другой допустимый тип, который вы хотите принять.
Например, вот 3 метода, которые очень похожи и отличаются только входными массивами:
// Print all elements in an array of Integers public static void printArray(Integer[] arr) { for (Integer el : arr) System.out.printf("%s ", el); System.out.println(); } // Print all elements in an array of Doubles public static void printArray(Double[] arr) { for (Double el : arr) System.out.printf("%s ", el); System.out.println(); } // Print all elements in an array of Chars public static void printArray(Character[] arr) { for (Character el : arr) System.out.printf("%s ", el); System.out.println(); }
Используя дженерик, мы можем абстрагироваться и провести рефакторинг:
// Print all elements in an array, whether it be chars, doubles, or integers. public static <T> void printArray(T[] arr) { for (T el : arr) System.out.printf("%s ", el); System.out.println(); }
Обобщения устраняют необходимость приведения типов. Например:
import java.util.ArrayList; public class GenericTypeCastTest { public static void main(String[] args) { // Following requires typecasting ArrayList list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); System.out.printf("%s%n", s); // Following uses generics and requires no typecasting ArrayList<String> list2 = new ArrayList<String>(); list2.add("world"); String s2 = list2.get(0); System.out.printf("%s%n", s2); } } OUTPUT: hello world
Кроме того, первый пример, требующий приведения типов, выдает предупреждающее сообщение:
1 warning found: File: C:\PATH_TO_Java_File\GenericTypeCastTest.java [line: 9] Warning: unchecked call to add(E) as a member of the raw type java.util.ArrayList
Компилятор Java применяет строгую проверку типов при использовании дженериков. Это означает, что вместо отладки сложной ошибки времени выполнения мы получим ошибки времени компиляции.
Лично я считаю, что дженерики полезны для СУХОЙ обработки вашего кода, сводя к минимуму использование перегруженных (и повторяющихся) методов. Вы можете использовать дженерики не только для методов, но и для классов, интерфейсов и типов.
Я не буду рассматривать следующие понятия, но если вы хотите углубиться в них: общие подстановочные знаки, ограниченные и неограниченные подстановочные знаки, верхняя граница и нижняя граница, автобокс, необработанный тип и стирание.