Найти тему
learn Java

Java generics. То что спрашивают на собеседовании.

Оглавление

Дженерики спрашивают на собеседовании, поэтому предлагаю рассмотреть что это такое.

В чем суть Дженериков:

Под дженериками в Java подразумевают возможность добавлять к типам типы-параметры. Таким образом получаются сложные составные типы. Такой составной тип в общем случае выглядит так:

ОсновнойТип <ТипПараметр>

Пример:
ArrayList
<Integer> list = new ArrayList<Integer>();

В данном примере мы создаем список list, который будет хранить в себе параметры типа int.

Если же мы создадим список без Дженериков, то в наш список можно будет сохранять параметры любых типов.
Пример:

Такой код скомпелируется и выведет все значения.
Такой код скомпелируется и выведет все значения.

Как это работает?

Все классы в Java унаследованы от класса Object
Поэтому к переменной типа Object можно присвоить любое значение:

  1. Object o = new Scanner(System.in); // В переменную o сохранена ссылка на объект типа Scanner
  2. Object o = new String(); // В переменную o сохранена ссылка на объект типа String
  3. Object o = new Integer(15); // В переменную o сохранена ссылка на объект типа Integer
  4. Object o = "Привет"; // В переменную o сохранена ссылка на объект типа String

Но при этом компилятор не следит за тем, объект какого именно типа был сохранен в переменную типа Object, поэтому вызвать методы, которые были у сохраненного объекта, но которых нет у переменной типа Object нельзя.
Пример:

Object o = new Scanner(System.in);
int x = o.nextInt();

Данный код вызовет ошибку, так как у класса Object нет метода nextInt().

Как сделать правильно в таком случае:

Object o = new Scanner(System.in);
Scanner console =
(Scanner) o; // выделен оператор приведения типа
int x = console.nextInt();

В данном примере мы сохраняем ссылку на объект типа Scanner в переменную типа Scanner с помощью оператора приведения типа (Scanner).

Просто так переменную типа Object нельзя присвоить переменной типа Scanner, даже если переменная типа Object хранит ссылку на объект типа Scanner. Поэтому мы используем оператор приведения типа.

Зачем нужны Дженерики?

Когда Java-разработчики только создавали класс ArrayList, они хотели сделать его универсальным, чтобы в нем можно было хранить объекты любого типа. Поэтому для хранения элементов они воспользовались массивом типа Object.

Сильная сторона такого подхода в том, что в коллекцию можно добавить объект любого типа.

Ну а слабых сразу несколько.

Недостаток 1.

Всегда приходилось писать оператор преобразования типа, когда доставали элементы из коллекции.

Недостаток 2.

Не было гарантии, что в коллекции хранятся элементы определенного типа

Данные в коллекцию могут заполняться где угодно:

  • в другом методе
  • в другой программе
  • загружаться из файла
  • получаться по сети

Недостаток 3.

Данные коллекции можно случайно поменять по незнанию.

Вы можете передать коллекцию, заполненную вашими данными в какой-то метод, а этот метод, написанный совсем другим программистом, добавит в вашу коллекцию свои данные.

По названию коллекции непонятно, какие именно типы данных можно в ней хранить. А даже если и дать переменной такое название, ссылку на нее можно передать в десяток методов, и уж там-то точно об изначальном имени переменной ничего не будет известно.

Дженерики устраняют данные проблемы.

Спасибо за внимание.

В конце предлагаю рассмотреть простецкие задачки на понимание сути Дженериков:

generics.java
generics2.java