Generics & Внутренние классы ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-21

Дженерики или обобщения в Java

Положите в список без дженериков строку "42", а достаньте её как число: Integer n = (Integer) list.get(0); — компилятор промолчит, а программа упадёт во время выполнения с ClassCastException. Дженерики переносят эту ошибку туда, где её дешевле всего найти, — на этап компиляции.

Дженерики (generics, обобщения) в Java — это механизм параметризации типов: класс, интерфейс или метод объявляется с параметром типа (обычно T), а конкретный тип подставляется при использовании, например List<String> или Matrix<Integer>. Один и тот же код работает с разными типами данных, а компилятор проверяет типы и сам вставляет приведения. Дженерики появились в Java 5.

Классы, интерфейсы и методы с параметрами типа называют обобщёнными или параметризованными: обобщённый класс, обобщённый метод. Тип с подставленным аргументом, например Matrix<Integer>, называют параметризованным типом.

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

Дженерики нужны для безопасности типов: без них коллекция хранит Object, программист вручную приводит элементы к нужному типу, и ошибка приведения проявляется только во время выполнения. С дженериками компилятор знает тип элементов, не пропускает неподходящие значения и избавляет от явных приведений.

import java.util.ArrayList;
import java.util.List;

public class WhyGenerics {
    public static void main(String[] args) {
        // Без дженериков: сырой тип (raw type)
        List rawList = new ArrayList();
        rawList.add("42");
        Integer n = (Integer) rawList.get(0); // компилируется, но падает при запуске

        // С дженериками
        List<Integer> numbers = new ArrayList<>();
        // numbers.add("42");  // ошибка компиляции: incompatible types: String cannot be converted to Integer
        numbers.add(42);
        Integer m = numbers.get(0); // приведение не нужно
    }
}
// Вывод:
// Exception in thread "main" java.lang.ClassCastException:
//   class java.lang.String cannot be cast to class java.lang.Integer ...

Тип без аргументов в угловых скобках (List вместо List<Integer>) называется сырым типом (raw type). Он оставлен только для совместимости с кодом, написанным до Java 5, — в новом коде его не используют.

Что означает T в Java: параметр типа

T в Java — это имя параметра типа, заполнитель, вместо которого при использовании класса или метода подставляется конкретный ссылочный тип. В объявлении class Matrix<T> буква T — параметр типа, а в Matrix<Integer> тип Integer — аргумент типа.

Параметр типа объявляется в угловых скобках после имени класса или перед типом возвращаемого значения метода. Внутри класса T используется как обычный тип: для полей, параметров, возвращаемых значений и локальных переменных. Имя может быть любым идентификатором, но по соглашению используют одну заглавную букву:

Имя Расшифровка Где встречается Пример из JDK
T Type — любой тип Обобщённые классы и методы Optional<T>, Comparable<T>
E Element — элемент Коллекции List<E>, Set<E>
K, V Key, Value — ключ и значение Словари Map<K, V>
N Number — число Числовые параметры <N extends Number>
R Result — результат Функциональные интерфейсы Function<T, R>
S, U Второй, третий тип Несколько параметров BiFunction<T, U, R>

Аргументом типа может быть только ссылочный тип. Вместо примитивов используют классы-обёртки: Integer вместо int, Double вместо double; значения преобразуются автоматически благодаря автоупаковке. Начиная с Java 7 аргументы типа в правой части объявления можно не повторять — достаточно diamond-оператора <>: Matrix<Integer> m = new Matrix<>(...).

Параметризованные классы

Параметризованный (обобщённый) класс — это класс, объявленный с одним или несколькими параметрами типа в угловых скобках: class Matrix<T>. Его код пишется один раз, а при создании объекта указывается конкретный тип: Matrix<Double>, Matrix<Integer>.

Пример обобщённого класса, который хранит элементы матрицы в массиве:

public class Matrix<T> {
    private final T[] array;

    public Matrix(T[] array) {
        this.array = array.clone();
    }

    public T get(int index) {
        return array[index];
    }

    public int size() {
        return array.length;
    }

    public static void main(String[] args) {
        Matrix<Double> doubleMatrix = new Matrix<>(new Double[]{1.5, 2.5});
        Matrix<Integer> integerMatrix = new Matrix<>(new Integer[]{1, 2, 3, 4});

        Integer first = integerMatrix.get(0); // приведение не нужно
        System.out.println(first + " " + doubleMatrix.size());
    }
}
// Вывод: 1 2

В объявлении Matrix<Integer> integerMatrix тип Integer является аргументом типа. Java не создаёт отдельную версию класса для каждого аргумента: во время выполнения существует только один класс Matrix (почему так — в разделе Стирание типов).

Дженерики работают только с объектами, поэтому примитивный тип в качестве аргумента не скомпилируется:

Matrix<int> intMatrix = new Matrix<>(new int[3]);
// Ошибка компиляции: unexpected type; required: reference, found: int

Параметризованные типы с разными аргументами несовместимы между собой. doubleMatrix и integerMatrix оба являются экземплярами Matrix, но для компилятора это разные типы:

doubleMatrix = integerMatrix;
// Ошибка компиляции: incompatible types: Matrix<Integer> cannot be converted to Matrix<Double>

Обобщённый класс может иметь любое количество параметров типа — они перечисляются через запятую:

public class TwoGen<T, V> {
    private final T obT;
    private final V obV;

    public TwoGen(T obT, V obV) {
        this.obT = obT;
        this.obV = obV;
    }

    public void showTypes() {
        System.out.println("Тип T: " + obT.getClass().getName());
        System.out.println("Тип V: " + obV.getClass().getName());
    }

    public T getObT() {
        return obT;
    }

    public V getObV() {
        return obV;
    }
}

public class SimpleGen {
    public static void main(String[] args) {
        TwoGen<Integer, String> twoGen = new TwoGen<>(88, "Generics");

        twoGen.showTypes();
        System.out.println("Значение T: " + twoGen.getObT());
        System.out.println("Значение V: " + twoGen.getObV());
    }
}
// Вывод:
// Тип T: java.lang.Integer
// Тип V: java.lang.String
// Значение T: 88
// Значение V: Generics

Ограниченные типы: <T extends Number>

Ограниченный тип (bounded type) — это параметр типа с верхней границей: запись <T extends Number> разрешает подставлять вместо T только Number и его подклассы. Взамен внутри класса можно вызывать методы границы, например doubleValue(), которых у произвольного Object нет.

Общий вид объявления — параметр типа, ключевое слово extends и суперкласс-граница:

class Gen<T extends Superclass> 

Пример: класс вычисляет среднее значение массива любых чисел.

public class Average<T extends Number> {
    private final T[] array;

    public Average(T[] array) {
        this.array = array;
    }

    public double average() {
        double sum = 0.0;

        for (T value : array) {
            sum += value.doubleValue(); // метод Number доступен благодаря границе
        }

        return sum / array.length;
    }
}

public class AverageExample {
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        Average<Integer> integerAverage = new Average<>(intArray);
        System.out.println("Среднее значение для Integer: " + integerAverage.average());

        Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5};
        Average<Double> doubleAverage = new Average<>(doubleArray);
        System.out.println("Среднее значение для Double: " + doubleAverage.average());

        // Не скомпилируется, потому что String не является наследником Number:
        // String[] strArray = {"1", "2", "3"};
        // Average<String> strAverage = new Average<>(strArray);
        // error: type argument String is not within bounds of type-variable T
    }
}
// Вывод:
// Среднее значение для Integer: 3.0
// Среднее значение для Double: 3.3

Границей может быть не только класс, но и интерфейс — для интерфейса тоже пишется extends, а не implements:

public class MyClass<T extends Serializable> 

Ограничение может включать класс и один или несколько интерфейсов, объединённых знаком &. Класс указывается первым:

class Gen<T extends MyClass & MyInterface1 & MyInterface2>

Здесь аргументом типа может быть только подкласс MyClass, который реализует оба интерфейса — MyInterface1 и MyInterface2.

Метасимвольный аргумент (wildcard ?)

Метасимвольный аргумент (wildcard) — это знак ? на месте аргумента типа, обозначающий неизвестный тип. Он нужен, когда метод должен принимать параметризованный тип с любым аргументом: Average<?> принимает и Average<Integer>, и Average<Double>.

Допустим, в класс Average из раздела Ограниченные типы нужно добавить метод sameAvg(), который сравнивает средние значения двух массивов, причём типы массивов могут различаться. Первая мысль — использовать параметр T:

boolean sameAvg(Average<T> ob) {
    return average() == ob.average();
}

Такой метод примет только объект с тем же аргументом типа: Average<Integer> можно сравнить с Average<Integer>, а вызов с Average<Double> не скомпилируется. Решение — метасимвольный аргумент:

boolean sameAvg(Average<?> ob) {
    return average() == ob.average();
}

Метасимвол не влияет на то, какие объекты Average можно создавать, — это по-прежнему определяет граница extends Number в объявлении класса. Он лишь совпадает с любым допустимым объектом Average:

public class AverageExample2 {
    public static void main(String[] args) {
        Average<Integer> iob = new Average<>(new Integer[]{1, 2, 3, 4, 5});
        Average<Double> dob = new Average<>(new Double[]{1.1, 2.2, 3.3, 4.4, 5.5});
        Average<Float> fob = new Average<>(new Float[]{1.0F, 2.0F, 3.0F, 4.0F, 5.0F});

        System.out.println("iob и dob: " + (iob.sameAvg(dob) ? "одинаковые" : "разные"));
        System.out.println("iob и fob: " + (iob.sameAvg(fob) ? "одинаковые" : "разные"));
    }
}
// Вывод:
// iob и dob: разные
// iob и fob: одинаковые

Метасимвол можно ограничить сверху или снизу. Это особенно важно, когда метод работает с иерархией классов: List<Integer> не является подтипом List<Number>, хотя Integer — подкласс Number, а List<? extends Number> принимает оба.

Запись Что принимает Чтение элементов Добавление элементов
List<?> Список с любым аргументом типа Как Object Нельзя (кроме null)
List<? extends Number> List<Number>, List<Integer>, List<Double>... Как Number Нельзя (кроме null)
List<? super Integer> List<Integer>, List<Number>, List<Object> Только как Object Можно добавлять Integer
import java.util.ArrayList;
import java.util.List;

public class WildcardExample {
    // ? extends Number: из списка можно читать элементы как Number
    static double sum(List<? extends Number> list) {
        double sum = 0;
        for (Number n : list) {
            sum += n.doubleValue();
        }
        // list.add(1); // ошибка компиляции: точный тип элементов неизвестен
        return sum;
    }

    // ? super Integer: в список можно добавлять Integer
    static void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 3; i++) {
            list.add(i);
        }
    }

    public static void main(String[] args) {
        List<Integer> ints = List.of(1, 2, 3);
        List<Double> doubles = List.of(1.5, 2.5);
        System.out.println(sum(ints));
        System.out.println(sum(doubles));

        List<Number> numbers = new ArrayList<>();
        addNumbers(numbers);
        System.out.println(numbers);

        // List<Number> wrong = ints;
        // ошибка компиляции: incompatible types: List<Integer> cannot be converted to List<Number>
    }
}
// Вывод:
// 6.0
// 4.0
// [1, 2, 3]

Параметризованные методы и конструкторы

Параметризованный (обобщённый) метод — это метод, который объявляет собственные параметры типа в угловых скобках перед типом возвращаемого значения: public static <T> boolean isIn(T x, T[] array). Такой метод можно объявить и в обычном, необобщённом классе; аргумент типа компилятор обычно выводит сам по аргументам вызова.

Методы обобщённого класса, использующие параметр класса T, тоже работают с параметром типа, но собственных параметров не объявляют. Параметризованный метод независим от класса:

public class GenMethodExample {
    /**
     * Проверяет, является ли объект x элементом массива array.
     *
     * @param x     искомый объект
     * @param array массив, в котором выполняется поиск
     * @param <T>   тип искомого объекта и элементов массива
     * @return true, если x равен одному из элементов массива
     */
    public static <T> boolean isIn(T x, T[] array) {
        for (T element : array) {
            if (x.equals(element)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};

        if (isIn(2, intArray)) {
            System.out.println("2 входит в массив intArray");
        }
        if (!isIn(7, intArray)) {
            System.out.println("7 не входит в массив intArray");
        }

        String[] strArray = {"one", "two", "three", "four", "five"};

        if (isIn("two", strArray)) {
            System.out.println("two входит в массив strArray");
        }
        if (!isIn("seven", strArray)) {
            System.out.println("seven не входит в массив strArray");
        }
    }
}
// Вывод:
// 2 входит в массив intArray
// 7 не входит в массив intArray
// two входит в массив strArray
// seven не входит в массив strArray

Неочевидный момент

Вызов isIn("2", intArray) скомпилируется и вернёт false: компилятор выведет T как общий супертип String и Integer (пересечение Serializable & Comparable<...>). Чтобы такая ошибка ловилась при компиляции, укажите аргумент типа явно: GenMethodExample.<Integer>isIn("2", intArray) — теперь это ошибка incompatible types: String cannot be converted to Integer.

Конструкторы тоже могут быть обобщёнными, даже если их класс таковым не является:

public class GenConstructor {
    private final double value;

    public <T extends Number> GenConstructor(T arg) {
        value = arg.doubleValue();
    }

    public void showValue() {
        System.out.println("value: " + value);
    }
}

public class GenConstructorExample {
    public static void main(String[] args) {
        GenConstructor genConstructor1 = new GenConstructor(100);
        GenConstructor genConstructor2 = new GenConstructor(123.5F);

        genConstructor1.showValue();
        genConstructor2.showValue();
    }
}
// Вывод:
// value: 100.0
// value: 123.5

Параметризованные интерфейсы

Параметризованный (обобщённый) интерфейс объявляется так же, как обобщённый класс: interface MyInterface<T>. В JDK так устроены Comparable<T>, Comparator<T>, List<E>. Класс, реализующий такой интерфейс, либо сам становится обобщённым и передаёт свой параметр интерфейсу, либо подставляет конкретный тип.

public interface MyInterface<T> {
    T someMethod(T t);
}

// 1. Обобщённый класс передаёт свой параметр T интерфейсу
public class Echo<T> implements MyInterface<T> {
    @Override
    public T someMethod(T t) {
        return t;
    }
}

// 2. Обычный класс фиксирует аргумент типа
public class Upper implements MyInterface<String> {
    @Override
    public String someMethod(String s) {
        return s.toUpperCase();
    }
}

public class InterfaceExample {
    public static void main(String[] args) {
        MyInterface<String> echo = new Echo<>();
        MyInterface<String> upper = new Upper();
        System.out.println(echo.someMethod("some string"));
        System.out.println(upper.someMethod("some string"));
    }
}
// Вывод:
// some string
// SOME STRING

Если класс реализует обобщённый интерфейс, но сам объявлен без параметра и не указывает аргумент (class Echo implements MyInterface<T>), компилятор выдаст ошибку cannot find symbol: class T.

Иерархии параметризованных классов

Параметризованные классы участвуют в наследовании так же, как обычные: обобщённый класс может быть суперклассом или подклассом. Главное правило — подкласс обязан передать вверх по иерархии все аргументы типа, которые требует обобщённый суперкласс: либо своим параметром, либо конкретным типом.

public class GenericSuper<T> {
    private final T ob;

    public GenericSuper(T ob) {
        this.ob = ob;
    }

    public T getOb() {
        return ob;
    }
}

// 1. Подкласс передаёт свой параметр T суперклассу
public class GenericSub<T> extends GenericSuper<T> {
    public GenericSub(T ob) {
        super(ob);
    }
}

// 2. Необобщённый подкласс фиксирует аргумент типа
public class IntegerHolder extends GenericSuper<Integer> {
    public IntegerHolder(int value) {
        super(value);
    }
}

// 3. Подкласс добавляет собственный параметр типа
public class LabeledHolder<T, V> extends GenericSuper<T> {
    private final V label;

    public LabeledHolder(T ob, V label) {
        super(ob);
        this.label = label;
    }

    public V getLabel() {
        return label;
    }
}

public class HierarchyExample {
    public static void main(String[] args) {
        LabeledHolder<Double, String> price = new LabeledHolder<>(9.99, "цена");
        System.out.println(price.getLabel() + ": " + price.getOb());

        int value = new IntegerHolder(5).getOb(); // приведение не нужно
        System.out.println(value);
    }
}
// Вывод:
// цена: 9.99
// 5

Подкласс обобщённого суперкласса не обязан быть обобщённым (вариант 2), а при необходимости может объявить и собственные параметры типа (вариант 3). Обратное тоже допустимо: суперклассом обобщённого класса может быть обычный необобщённый класс.

Оператор instanceof и дженерики

Оператор instanceof проверяет класс объекта во время выполнения, а аргументы типа к этому моменту стёрты. Поэтому проверка instanceof GenericSub<?> работает всегда, а проверка с конкретным аргументом, например instanceof GenericSub<Integer>, разрешена только тогда, когда компилятор может гарантировать её корректность по статическому типу переменной.

public class InstanceofExample {
    public static void main(String[] args) {
        GenericSuper<Integer> object = new GenericSub<>(88);

        System.out.println(object instanceof GenericSuper<?>);
        System.out.println(object instanceof GenericSub<?>);

        // Java 16+: компилируется, приведение GenericSuper<Integer> -> GenericSub<Integer> проверяемое
        System.out.println(object instanceof GenericSub<Integer>);

        Object any = object;
        // Ошибка компиляции в любой версии Java:
        // System.out.println(any instanceof GenericSub<Integer>);
        // Java 16+: Object cannot be safely cast to GenericSub<Integer>
        // до Java 16: illegal generic type for instanceof
    }
}
// Вывод:
// true
// true
// true

Изменение в Java 16

До Java 16 любая проверка instanceof с конкретным аргументом типа была ошибкой компиляции, и в старых учебниках и тестах так и написано. Вместе с pattern matching для instanceof (JEP 394) правило смягчили: проверка разрешена, если приведение к целевому типу не является непроверяемым (unchecked). Удобно в паттерне: if (object instanceof GenericSub<Integer> sub) { int x = sub.getOb(); }.

Стирание типов (type erasure)

Стирание типов (type erasure) — это способ, которым в Java реализованы дженерики: компилятор проверяет аргументы типа, а затем удаляет их из байт-кода. Параметр T заменяется своей границей (Object, если границы нет, или Number для <T extends Number>), а в местах использования вставляются приведения типов. Во время выполнения JVM не знает, чем был T.

Так выглядит обобщённый класс до компиляции и (условно) после стирания:

// Исходный код
public class Box<T extends Number> {
    private T value;
    public T get() { return value; }
}
Box<Integer> box = new Box<>();
Integer i = box.get();

// Что остаётся после стирания
public class Box {
    private Number value;
    public Number get() { return value; }
}
Box box = new Box();
Integer i = (Integer) box.get(); // приведение вставил компилятор

Поэтому параметризованные типы с разными аргументами во время выполнения неразличимы — это один и тот же класс:

List<String> strings = new ArrayList<>();
List<Integer> numbers = new ArrayList<>();

System.out.println(strings.getClass() == numbers.getClass());
System.out.println(strings.getClass().getName());
// Вывод:
// true
// java.util.ArrayList

Стирание объясняет, почему нельзя написать new T(): в байт-коде на этом месте был бы new Object(), и JVM не знала бы, объект какого класса создать. Если тип нужен во время выполнения, его передают явно — объектом Class<T> (такой приём называют type token):

public class Factory<T> {
    private final Class<T> type;

    public Factory(Class<T> type) {
        this.type = type;
    }

    public T create() throws ReflectiveOperationException {
        return type.getDeclaredConstructor().newInstance(); // вместо недопустимого new T()
    }

    public static void main(String[] args) throws ReflectiveOperationException {
        Factory<StringBuilder> factory = new Factory<>(StringBuilder.class);
        StringBuilder sb = factory.create();
        System.out.println(sb.append("создан через Class<T>"));
    }
}
// Вывод: создан через Class<T>

Где информация о типе всё-таки сохраняется: getGenericSuperclass()

Стираются аргументы типа у объектов, но не в объявлениях классов, полей и методов. Если класс наследует параметризованный суперкласс с конкретным аргументом, этот аргумент записан в class-файл, и его можно прочитать через рефлексию методом Class.getGenericSuperclass():

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class SuperclassTypeExample {
    public static void main(String[] args) {
        // IntegerHolder extends GenericSuper<Integer> (из раздела об иерархиях)
        Type type = IntegerHolder.class.getGenericSuperclass();
        System.out.println(type);

        Type argument = ((ParameterizedType) type).getActualTypeArguments()[0];
        System.out.println(argument);
    }
}
// Вывод:
// GenericSuper<java.lang.Integer>
// class java.lang.Integer

На этом построен приём super type token: создают анонимный подкласс абстрактного обобщённого класса, например new TypeReference<List<String>>() {}, и в его конструкторе вызывают getClass().getGenericSuperclass(). Так библиотеки Jackson (TypeReference) и Spring (ParameterizedTypeReference) узнают полный тип List<String>, который через List.class передать невозможно.

Ограничения дженериков

Почти все ограничения дженериков в Java — следствие стирания типов: то, что требует знать T во время выполнения (создание объекта или массива, проверка типа), запрещено, а к статическим членам параметр типа класса не относится вовсе. Сводная таблица с точными сообщениями компилятора javac:

Что нельзя Пример Сообщение компилятора Как обойти
Примитив как аргумент типа Matrix<int> unexpected type; required: reference, found: int Класс-обёртка: Matrix<Integer>
Создать объект по параметру типа new T() unexpected type; required: class, found: type parameter T Передать Class<T> или Supplier<T>
Создать массив по параметру типа new T[10] generic array creation Получить готовый массив в конструкторе или использовать List<T>
Массив параметризованного типа new GenericSub<Integer>[10] generic array creation GenericSub<?>[] или List<GenericSub<Integer>>
Использовать T класса в статическом поле или методе static T ob; non-static type variable T cannot be referenced from a static context Статический метод со своим параметром: static <V> void m(V v)
Перегрузка, различающаяся только аргументом типа print(List<String>) и print(List<Integer>) name clash: ... have the same erasure Дать методам разные имена
Обобщённый класс исключения class MyEx<T> extends Exception a generic class may not extend java.lang.Throwable Хранить данные в обычном поле исключения

Те же ограничения в коде:

public class GenRestriction<T> {
    private T ob;
    private T[] array;

    // Неверно: статическое поле не может иметь тип T
    // public static T staticOb;

    public GenRestriction(T ob, T[] array) {
        // Недопустимо:
        // this.ob = new T();
        // this.array = new T[10];
        this.ob = ob;
        this.array = array; // массив создаёт вызывающий код, где тип известен
    }

    // Неверно: статический метод не может использовать T класса
    // public static T getStaticOb() { return null; }

    // А статический обобщённый метод со своим параметром типа объявить можно
    public static <V> void print(V v) {
        System.out.println(v);
    }

    public static void main(String[] args) {
        // GenericSub<Integer>[] gens = new GenericSub<Integer>[10]; // generic array creation
        GenericSub<?>[] gens = new GenericSub<?>[10];
        gens[0] = new GenericSub<>(34);
        print(gens[0].getOb());
    }
}
// Вывод: 34

Часто задаваемые вопросы

Чем дженерики Java отличаются от шаблонов C++ и generics в C#?

Шаблоны C++ генерируют отдельный код для каждого аргумента типа, а в C# аргументы типа сохраняются во время выполнения (reified generics), поэтому там допустимы List<int> и new T(). В Java существует один класс на все аргументы, а сами аргументы стираются после проверки компилятором. Подробнее — в разделе Стирание типов.

Почему List<Integer> нельзя передать в метод с параметром List<Number>?

Параметризованные типы инвариантны. Если бы List<Integer> был подтипом List<Number>, метод мог бы добавить в него Double и сломать список целых чисел. Чтобы метод принимал списки любых чисел, объявите параметр как List<? extends Number> — см. раздел Метасимвольный аргумент.

Что такое правило PECS?

PECS — Producer Extends, Consumer Super. Если параметр только отдаёт элементы методу (производитель), используйте ? extends T; если только принимает их (потребитель) — ? super T. Классический пример из JDK — Collections.copy(List<? super T> dest, List<? extends T> src).

Что получится, если написать var list = new ArrayList<>()?

Типом переменной станет ArrayList<Object>: при var слева нет типа, из которого diamond-оператор мог бы вывести аргумент. В такой список можно положить что угодно, и проверка типов теряется. Пишите var list = new ArrayList<String>() или объявляйте тип явно.

Почему компилятор пишет «uses unchecked or unsafe operations»?

Это предупреждение о непроверяемых операциях: в коде используется сырой тип (например, List без угловых скобок) или приведение к параметризованному типу, которое нельзя проверить во время выполнения. Подробности покажет компиляция с ключом -Xlint:unchecked. Исправление — указать аргументы типа; @SuppressWarnings("unchecked") ставят только там, где безопасность доказана вручную.

Practice

Reinforce this lesson with hands-on tasks, each with a level and a time estimate.

All module tasks →

Video Explanation

Prefer video format? Watch this lesson with examples and explanations.

Comments

Please log in or register to have a possibility to add comment.