Рубрики
Без рубрики

Предупреждение Java “Непроверенное приведение”

Автор оригинала: Kai Yuan.

1. Обзор

Иногда, когда мы компилируем наши исходные файлы Java, мы видим предупреждающие сообщения ” непроверенное приведение “, напечатанные компилятором Java.

В этом уроке мы подробнее рассмотрим предупреждающее сообщение. Мы обсудим, что означает это предупреждение, почему нас предупреждают и как решить проблему.

Некоторые компиляторы Java по умолчанию подавляют непроверенные предупреждения.

Давайте убедимся, что мы включили опцию компилятора для печати “непроверенных” предупреждений , прежде чем мы рассмотрим это ” непроверенное приведение ” предупреждение.

2. Что означает предупреждение “непроверенный бросок”?

Непроверенное приведение ” является предупреждением во время компиляции . Проще говоря, мы увидим это предупреждение при приведении необработанного типа к параметризованному типу без проверки типа .

Пример может объяснить это прямолинейно. Допустим, у нас есть простой метод возврата необработанного типа Map :

public class UncheckedCast {
    public static Map getRawMap() {
        Map rawMap = new HashMap();
        rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
        rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
        rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
        return rawMap;
    }
...
}

Теперь давайте создадим тестовый метод для вызова метода, описанного выше, и приведем результат к Map LocalDate> : LocalDate>

@Test
public void givenRawMap_whenCastToTypedMap_shouldHaveCompilerWarning() {
    Map castFromRawMap = (Map) UncheckedCast.getRawMap();
    Assert.assertEquals(3, castFromRawMap.size());
    Assert.assertEquals(castFromRawMap.get("date 2"), LocalDate.of(1992, Month.AUGUST, 8));
}

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

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

$ mvn clean test
...
[WARNING] .../src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java:[14,97] unchecked cast
  required: java.util.Map
  found:    java.util.Map
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
...
[INFO] Results:
[INFO] 
[INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

Как показывает вывод Maven, мы успешно воспроизвели предупреждение.

С другой стороны, наш тест работает без каких-либо проблем, даже если мы видим предупреждение компилятора ” непроверенное приведение “.

Мы знаем, что компилятор не предупредит нас без причины. Должно быть, есть какая-то потенциальная проблема, когда мы видим это предупреждение.

Давайте разберемся.

3. Почему компилятор Java Предупреждает Нас?

Наш метод тестирования отлично работает в предыдущем разделе, хотя мы видим предупреждение ” непроверенное приведение “. Это связано с тем, что когда мы приводили необработанный тип Map в Map , LocalDate> , необработанная Карта содержит только LocalDate> записи. То есть, типизация безопасна. , LocalDate>

Чтобы проанализировать потенциальную проблему, давайте немного изменим метод getrowmap () , добавив еще одну запись в необработанный тип Карта :

public static Map getRawMapWithMixedTypes() {
    Map rawMap = new HashMap();
    rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
    rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
    rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
    rawMap.put("date 4", new Date());
    return rawMap;
}

На этот раз мы добавили новую запись в Карту с типом <Строка, Дата> в методе выше.

Теперь давайте напишем новый метод тестирования для вызова метода get Raw Map Со смешанными типами() :

@Test(expected = ClassCastException.class)
public void givenMixTypedRawMap_whenCastToTypedMap_shouldThrowClassCastException() {
    Map castFromRawMap = (Map) UncheckedCast.getRawMapWithMixedTypes();
    Assert.assertEquals(4, castFromRawMap.size());
    Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3")));
}

Если мы скомпилируем и запустим тест, снова будет напечатано предупреждающее сообщение ” непроверенное приведение “. Кроме того, наш тест пройдет.

Однако, поскольку наш тест имеет expected.class аргумент, это означает, что метод тестирования выдал ClassCastException .

Если мы рассмотрим это поближе, исключение ClassCastException не возникает в строке приведения необработанного типа Map к Map<Строка, LocalDate> , хотя предупреждающее сообщение указывает на эту строку. Вместо этого исключение возникает, когда мы получаем данные с неправильным типом с помощью ключа : , приведенного из необработанной карты.get(“дата 4”).

Если мы приведем необработанную коллекцию типов, содержащую данные с неправильными типами, к коллекции параметризованных типов, исключение ClassCastException не будет создано, пока мы не загрузим данные с неправильным типом .

Иногда мы можем получить исключение слишком поздно.

Например, мы получаем необработанный тип Map со многими записями, вызывая наш метод, а затем приводим его к Map с параметризованным типом:

(Map) UncheckedCast.getRawMapWithMixedTypes()

Для каждой записи в Карте нам нужно отправить Локальные данные объекта в удаленный API. До тех пор , пока мы не столкнемся с ClassCastException , весьма вероятно, что уже было сделано много вызовов API. В зависимости от требований могут потребоваться некоторые дополнительные процессы восстановления или очистки данных.

Было бы хорошо, если бы мы могли получить исключение раньше, чтобы мы могли решить, как справиться с обстоятельствами записей с неправильными типами.

Поскольку мы понимаем потенциальную проблему, стоящую за предупреждением ” непроверенный бросок “, давайте посмотрим, что мы можем сделать, чтобы решить эту проблему.

4. Что Мы Должны Делать С Предупреждением?

4.1. Избегайте Использования Необработанных Типов

Дженерики были введены с Java 5. Если наша среда Java поддерживает универсальные типы, нам следует избегать использования необработанных типов. Это связано с тем, что использование необработанных типов приведет к тому, что мы потеряем все преимущества дженериков в плане безопасности и выразительности.

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

Однако иногда нам приходится работать с некоторыми старыми библиотеками. Методы из этих старых внешних библиотек могут возвращать необработанные коллекции типов.

Вызов этих методов и приведение к параметризованным типам приведет к появлению предупреждения компилятора ” непроверенное приведение “. Но у нас нет контроля над внешней библиотекой.

Далее давайте посмотрим, как вести это дело.

4.2. Подавите предупреждение “непроверено” .

Если мы не можем устранить предупреждение ” непроверенное приведение “, и мы уверены, что код, вызывающий предупреждение, является типобезопасным, мы можем подавить предупреждение , используя аннотацию SuppressWarnings(“непроверенный”) .

Когда мы используем @ Предупреждения о подавлении(“непроверено”) аннотация, мы всегда должны помещать ее в минимально возможную область.

Давайте рассмотрим метод remove() из класса ArrayList в качестве примера:

public E remove(int index) {
    Objects.checkIndex(index, size);
    final Object[] es = elementData;
                                                              
    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    fastRemove(es, index);
                                                              
    return oldValue;
}

4.3. Проверка Сохранности Типов Перед Использованием Коллекции Необработанных Типов

Как мы узнали, @Suppresswarnings(“непроверено”) аннотация просто подавляет предупреждающее сообщение, фактически не проверяя, является ли приведение типобезопасным.

Если мы не уверены, что приведение необработанного типа безопасно для типа, мы должны проверить типы, прежде чем мы действительно будем использовать данные , чтобы мы могли получить исключение ClassCastException ранее .

5. Заключение

В этой статье мы узнали, что означает предупреждение компилятора ” непроверенное приведение “.

Кроме того, мы рассмотрели причину этого предупреждения и способы решения потенциальной проблемы.

Как всегда, весь код в этой записи доступен на GitHub .