Spec-Zone .ru
спецификации, руководства, описания, API
|
Эта страница затрагивает следующие темы:
Наиболее параметризованные типы, такой как 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
.
Рассмотрите метод 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
вызывается; в этом примере предупреждение сгенерировано для class HeapPollutionExample
. Эти компиляторы не генерируют предупреждение на сайте объявления. Однако, Java, SE 7 генерирует предупреждение и на сайте объявления и на сайте вызова (если предупреждения не вытесняются с аннотациями; см. Предупреждения Подавления от Методов Varargs с Формальными параметрами Non-Reifiable для получения дополнительной информации). Преимущество генерирования предупреждения, когда компилятор встречается с varargs методом, у которого есть non-reifiable varargs формальный параметр на сайте объявления в противоположность сайту вызова, состоит в том, что есть только один сайт объявления; есть потенциально много сайтов вызова.
Метод 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 метод, который параметризовал параметры, и Вы гарантируете, что тело метода не бросает a ClassCastException
или другое подобное исключение из-за неподходящей обработки varargs формального параметра (как показано в ArrayBuilder.faultyMethod
метод), можно подавить предупреждение, что компилятор генерирует для этих видов varargs методов при использовании одной из следующих опций:
Добавьте следующую аннотацию к объявления метода неконструктора и статическому:
@SafeVarargs
В отличие от этого @SuppressWarnings
аннотация, @SafeVarargs
аннотация является задокументированной частью контракта метода; эта аннотация утверждает, что реализация метода не будет ненадлежащим образом обрабатывать varargs формальный параметр.
Добавьте следующую аннотацию к объявлению метода:
@SuppressWarnings({"unchecked", "varargs"})
В отличие от этого @SafeVarargs
аннотация, @SuppressWarnings("varargs")
не подавляет предупреждения, сгенерированные от сайта вызова метода.
Используйте опцию компилятора -Xlint:varargs
.
Например, следующая версия ArrayBuilder
У class есть два дополнительных метода, 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 генерирует следующие предупреждения для этого примера:
addToList
: [unchecked] Possible heap pollution from parameterized vararg type T
[unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
addToList2
: Когда метод вызывают (никакое предупреждение не сгенерировано в объявлении метода): [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
addToList3
: Никакие предупреждения не сгенерированы или в объявлении метода или когда его вызывают.Отметьте: В Java SE 5 и 6, это - ответственность программиста, который вызывает varargs метод, у которого есть non-reifiable varargs формальный параметр, чтобы определить, произошло ли бы загрязнение "кучи". Однако, если этот программист не писал такой метод, он или она не может легко определить это. В Java SE 7, это - ответственность программиста, который пишет эти виды varargs методов, чтобы гарантировать, что они должным образом обрабатывают varargs формальный параметр и гарантируют, что загрязнение "кучи" не происходит.