Какие типы данных можно использовать в Generics в Java?

Ответ

В Java Generics могут использоваться только ссылочные типы (reference types). Примитивные типы (int, char и т.д.) напрямую не поддерживаются, но их можно использовать через соответствующие классы-обертки (Integer, Character).

Почему только ссылочные типы? Generics реализованы через механизм стирания типов (type erasure) во время компиляции, который работает с Object и его подтипами. Примитивы не наследуются от Object.

Примеры допустимых типов:

  1. Классы:

    class Container<T> {
        private T item;
        public void set(T item) { this.item = item; }
        public T get() { return item; }
    }
    Container<String> stringContainer = new Container<>();
    Container<Integer> intContainer = new Container<>();
  2. Интерфейсы:

    List<String> list = new ArrayList<>();
    Map<Integer, String> map = new HashMap<>();
  3. Массивы (как тип):

    List<String[]> listOfArrays = new ArrayList<>();

Важно: Начиная с Java 7, для сокращения кода используется diamond operator <>, который позволяет компилятору вывести тип из контекста.

Ответ 18+ 🔞

Давай разберёмся с этим дженерик-делом, а то у некоторых мозг набекрень встаёт, как только видят эти угловые скобочки. Ну, типа, <T> там всякие.

Смотри, в Java эти ваши Generics — они, блядь, исключительно для ссылочных типов. То есть всякие String, Integer, MySuperPuperClass. А вот примитивы — int, char и прочая мелочь — нихуя не прокатят напрямую. Совсем. Как будто их нет, понимаешь?

А почему, спрашивается, такая хуйня? А потому что внутри, на этапе компиляции, происходит так называемое стирание типов (type erasure). Короче, компилятор берёт всё это <T> и превращает в Object, а потом уже кастит обратно, куда надо. А Object — он же отец всех классов, но примитивы — они сироты, блядь, не от него. Вот и вся магия, ёпта.

Но выход есть, не ссы! Берёшь классы-обёртки — Integer вместо int, Character вместо char — и вуаля, всё работает. Немного писанины добавится, но жить можно.

Примерчики, чтобы въехать:

  1. Свой класс, чтоб свой:

    class Container<T> {
        private T item;
        public void set(T item) { this.item = item; }
        public T get() { return item; }
    }
    // Строки — пожалуйста
    Container<String> stringContainer = new Container<>();
    // Целые числа — да не вопрос, через Integer
    Container<Integer> intContainer = new Container<>();
  2. Стандартные интерфейсы — там вообще красота:

    List<String> list = new ArrayList<>();
    Map<Integer, String> map = new HashMap<>();
  3. Массивы тоже можно, но осторожно:

    List<String[]> listOfArrays = new ArrayList<>();

Лайфхак на миллион, блядь: С седьмой Java появился алмазный оператор <>. Вместо того чтобы писать new ArrayList<String>(), можно просто new ArrayList<>(). Компилятор не дурак, он сам сообразит, что ты имел в виду. Экономия времени и нервов — овердохуища!