Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Улучшенные Предупреждения компилятора и Ошибки При использовании Формальных параметров Non-Reifiable с Методами Varargs

Эта страница затрагивает следующие темы:

Загрязнение "кучи"

Наиболее параметризованные типы, такой как ArrayList<Number> и List<String>, типы non-reifiable. Тип non-reifiable является типом, который не абсолютно доступен во время выполнения. Во время компиляции, non-reifiable типы подвергаются процессу, названному стиранием типа, во время которого компилятор удаляет информацию, связанную с параметрами типа, и введите параметры. Это гарантирует совместимость на уровне двоичных кодов библиотеками Java и приложениями, которые были созданы перед обобщениями. Поскольку стирание типа удаляет информацию из параметризованных типов во время компиляции, эти типы являются non-reifiable.

Загрязнение "кучи" происходит, когда переменная параметризованного типа обращается к объекту, который не имеет того параметризованного типа. Эта ситуация может только произойти, если бы программа, выполняемая некоторая работа, которая дала бы начало предупреждению непроверенному во время компиляции. Предупреждение непроверенное сгенерировано, если, любой во время компиляции (в рамках правил проверки типа времени компиляции) или во время выполнения, правильность работы, включающей параметризованный тип (например, бросок или вызов метода), не может быть проверен.

Рассмотрите следующий пример:

    List l = new ArrayList<Number>();
    List<String> ls = l;       // unchecked warning
    l.add(0, new Integer(42)); // another unchecked warning
    String s = ls.get(0);      // ClassCastException is thrown

Во время стирания типа, типов ArrayList<Number> и List<String> стать ArrayList и List, соответственно.

Переменная ls имеет параметризованный тип List<String>. Когда List ссылаемый l присваивается ls, компилятор генерирует предупреждение непроверенное; компилятор неспособен определить во время компиляции, и кроме того знает, что JVM не будет в состоянии определить во время выполнения, если l обращается к a List<String> введите; это не делает. Следовательно, загрязнение "кучи" происходит.

В результате во время компиляции компилятор генерирует другое предупреждение непроверенное в add оператор. Компилятор неспособен определить если переменная l обращается к a List<String> введите или a List<Integer> введите (и другая ситуация с загрязнением "кучи" происходит). Однако, компилятор не генерирует предупреждение или ошибку в get оператор. Этот оператор допустим; это вызывает List<String>.get метод, чтобы получить a String объект. Вместо этого во время выполнения, get оператор бросает a ClassCastException.

Подробно, ситуация с загрязнением "кучи" происходит когда List объект l, чей статический тип List<Number>, присваивается другому List объект, ls, у этого есть различный статический тип, List<String>. Однако, компилятор все еще позволяет это присвоение. Это должно позволить этому присвоению сохранять назад совместимость с версиями Java SE, которые не поддерживают обобщения. Из-за стирания типа, List<Number> и List<String> оба становятся List. Следовательно, компилятор позволяет присвоение объекта l, у которого есть необработанный тип List, к объекту ls.

Кроме того ситуация с загрязнением "кучи" происходит когда l.add метод вызывают. Статический тип второй формальный параметр add метод String, но этот метод вызывают с фактическим параметром различного типа, Integer. Однако, компилятор все еще позволяет этот вызов метода. Из-за стирания типа, типа второго формального параметра add метод (который определяется как List<E>.add(int,E)) становится Object. Следовательно, компилятор позволяет этот вызов метода потому что, после стирания типа, l.add метод может добавить любой объект типа Object, включая объект Integer введите, который является подтипом Object.

Переменные Методы Параметров и Формальные параметры Non-Reifiable

Рассмотрите метод ArrayBuilder.addToList в следующем примере. Это - переменные параметры (также известный как varargs) метод, который добавляет объекты типа T содержавшийся в elements формальный параметр varargs к List listArg:

import java.util.*;

public class ArrayBuilder {

  public static <T> void addToList (List<T> listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  public static void faultyMethod(List<String>... l) {
    Object[] objectArray = l;  // Valid
    objectArray[0] = Arrays.asList(new Integer(42));
    String s = l[0].get(0);    // ClassCastException thrown here
  }

}
import java.util.*;

public class HeapPollutionExample {

  public static void main(String[] args) {

    List<String> stringListA = new ArrayList<String>();
    List<String> stringListB = new ArrayList<String>();

    ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
    ArrayBuilder.addToList(stringListA, "Ten", "Eleven", "Twelve");
    List<List<String>> listOfStringLists = new ArrayList<List<String>>();
    ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);

    ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
  }
}

Java SE 7 компиляторов генерирует следующее предупреждение для определения метода ArrayBuilder.addToList:

warning: [varargs] Possible heap pollution from parameterized vararg type T

Когда компилятор встречается с varargs методом, он преобразовывает varargs формальный параметр в массив. Однако, язык программирования Java не разрешает создание массивов параметризованных типов. В методе ArrayBuilder.addToList, компилятор преобразовывает varargs формальный параметр T... elements к формальному параметру T[] elements, массив. Однако, из-за стирания типа, компилятор преобразовывает varargs формальный параметр в Object[] elements. Следовательно, есть возможность загрязнения "кучи". См. следующий раздел, Потенциальные Уязвимости Методов Varargs с Формальными параметрами Non-Reifiable, для получения дополнительной информации.

Отметьте: Java SE 5 и 6 компиляторов генерирует это предупреждение когда ArrayBuilder.addToList вызывается; в этом примере предупреждение сгенерировано для класса HeapPollutionExample. Эти компиляторы не генерируют предупреждение на сайте объявления. Однако, Java, SE 7 генерирует предупреждение и на сайте объявления и на сайте вызова (если предупреждения не вытесняются с аннотациями; см. Предупреждения Подавления от Методов Varargs с Формальными параметрами Non-Reifiable для получения дополнительной информации). Преимущество генерирования предупреждения, когда компилятор встречается с varargs методом, у которого есть non-reifiable varargs формальный параметр на сайте объявления в противоположность сайту вызова, состоит в том, что есть только один сайт объявления; есть потенциально много сайтов вызова.

Потенциальные Уязвимости Методов Varargs с Формальными параметрами Non-Reifiable

Метод ArrayBuilder.faultyMethod шоу, почему компилятор предупреждает Вас об этих видах методов. Первый оператор этого метода присваивает varargs формальный параметр l к Object массив objectArgs:

    Object[] objectArray = l;

Этот оператор может потенциально представить загрязнение "кучи". Значение, которое действительно соответствует параметризованный тип varargs формального параметра l может быть присвоен переменной objectArray, и таким образом может быть присвоен l. Однако, компилятор не генерирует предупреждение непроверенное в этом операторе. Компилятор уже генерировал предупреждение, когда он преобразовывал varargs формальный параметр List<String>... l к формальному параметру List[] l. Этот оператор допустим; переменная l имеет тип List[], который является подтипом Object[].

Следовательно, компилятор не выпускает предупреждение или ошибку, если Вы присваиваете a List объект любого типа к любому компоненту массива objectArray выстройте как показано этим оператором:

    objectArray[0] = Arrays.asList(new Integer(42));

Этот оператор присваивается к первому компоненту массива objectArray массив с a List объект, который содержит один объект типа Integer.

Предположите, что Вы вызываете ArrayBuilder.makeArray метод со следующим оператором:

    ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));

Во время выполнения JVM бросает a ClassCastException в следующем операторе:

    String s = l[0].get(0);    // ClassCastException thrown here

Объект хранится в первом компоненте массива переменной l имеет тип List<Integer>, но этот оператор ожидает объект типа List<String>.

Подавление Предупреждений от Методов Varargs с Формальными параметрами Non-Reifiable

Если Вы объявляете varargs метод, который параметризовал параметры, и Вы гарантируете, что тело метода не бросает a ClassCastException или другое подобное исключение из-за неподходящей обработки varargs формального параметра (как показано в ArrayBuilder.faultyMethod метод), можно подавить предупреждение, что компилятор генерирует для этих видов varargs методов при использовании одной из следующих опций:

Например, следующая версия ArrayBuilder у класса есть два дополнительных метода, addToList2 и addToList3:

public class ArrayBuilder {

  public static <T> void addToList (List<T> listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  @SuppressWarnings({"unchecked", "varargs"})
  public static <T> void addToList2 (List<T> listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  @SafeVarargs
  public static <T> void addToList3 (List<T> listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  // ...

}
public class HeapPollutionExample {

  // ...

  public static void main(String[] args) {

    // ...

    ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
    ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB);
    ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB);

    // ...

  }
}

Компилятор Java генерирует следующие предупреждения для этого примера:

Отметьте: В Java SE 5 и 6, это - ответственность программиста, который вызывает varargs метод, у которого есть non-reifiable varargs формальный параметр, чтобы определить, произошло ли бы загрязнение "кучи". Однако, если этот программист не писал такой метод, он или она не может легко определить это. В Java SE 7, это - ответственность программиста, который пишет эти виды varargs методов, чтобы гарантировать, что они должным образом обрабатывают varargs формальный параметр и гарантируют, что загрязнение "кучи" не происходит.


Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами