Java 101: Основната обиколка на езика Java, част 5

Предишна 1 2 Страница 2 Страница 2 от 2

Тип извод и родови конструктори за родови и не-родови класове

Общите и не-родовите класове могат да декларират общи конструктори, в които конструкторът има списък с параметри от формален тип. Например можете да декларирате следния родов клас с родов конструктор:

 public class Box { public  Box(T t) { // ... } } 

Тази декларация определя общ клас Boxс параметър формален тип E. Той също така указва общ конструктор с параметър формален тип T. Можете да създадете екземпляр на общия клас и да извикате неговия конструктор, както следва:

 new Box("Aggies") 

Този израз създава екземпляр от Box, преминавайки Marbleкъм E. Също така компилаторът извежда Stringкато Tаргумент на действителния тип, тъй като аргументът на конструктора е Stringобект.

Компилаторите Pre-Java 7 извеждат аргументи за действителния тип на генеричен конструктор, подобно на тези на генеричния метод. Компилаторът на Java 7 обаче може да направи извод за действителните аргументи на типа на родовия клас, който се създава в контекста на диамантен оператор. Помислете за следния пример:

 Box box = new Box("Aggies"); 

Освен че извежда типа за параметър Marbleза формален тип Eот родов клас Box, компилаторът извежда тип Stringза параметър за формален тип Tна конструктора на този общ клас.

Проектна монета малка промяна # 8: Опростено извикване на метод на varargs

Преди Java 7, всеки опит за извикване на метод varargs (променливи аргументи, известен също като променлива arity ) с нерефибируем тип varargs е причинил на компилатора да изведе предупреждение за "опасна операция". За да елиминира потенциала за много подобни предупредителни съобщения (по едно на сайт за повикване), Java 7 премести предупреждението от сайта за повикване в декларацията на метода.

Reifiable и не reifiable видове

А reifiable тип излага своята пълна информация за типа по време на изпълнение. Примерите включват примитивни типове, не генерични типове, сурови типове и извиквания на несвързани заместващи символи. За разлика от това, нерефибируемият тип има информация за типа, премахната по време на компилиране чрез изтриване на типа, за да се осигури двоична съвместимост с Java библиотеки и приложения, създадени преди генеричните продукти. Примерите включват Setи Set. Тъй като нерефибируем тип не е напълно достъпен по време на изпълнение, JVM не може да направи разлика между Setи Set; по време на изпълнение Setе наличен само суровият тип .

Общите методи, които включват входни параметри на vararg, могат да причинят замърсяване на купчина , при което променлива от параметризиран тип се отнася до обект, който не е от този параметризиран тип (например, ако суров тип е бил смесен с параметризиран тип). Компилаторът отчита „непроверено предупреждение“, тъй като коректността на операция, включваща параметризиран тип (като извикване на гласове или метод), не може да бъде проверена.

Листинг 13 демонстрира замърсяване на купчини в не-варагсов контекст.

Листинг 13. Демонстриране на замърсяване на купчини в не-варагсов контекст

 import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class HeapPollutionDemo { public static void main(String[] args) { Set s = new TreeSet(); Set ss = s; // unchecked warning s.add(new Integer(42)); // another unchecked warning Iterator iter = ss.iterator(); while (iter.hasNext()) { String str = iter.next(); // ClassCastException thrown System.out.println(str); } } } 

Променливата ssима параметризиран тип Set. Когато това, на java.util.Setкоето се позовава, sе присвоено ss, компилаторът генерира непроверено предупреждение. Прави това, защото компилаторът не може да определи, че се sотнася до Setтип (не го прави). Резултатът е замърсяване с купчини. (Компилаторът позволява това задание да запази обратната съвместимост със старите версии на Java, които не поддържат генерични продукти. Освен това изтриването на типа се преобразува Setв Set, което води до Setприсвояване на едно на друго Set.)

Компилаторът генерира втори нерегистриран предупреждение на реда, който се позовава Setе add()метод. Прави го, защото не може да определи дали променливата се sотнася до a Setили Setтип. Това е друга ситуация на замърсяване с купчини. (Компилаторът позволява този метод разговор, защото изтриване трансформации Setе boolean add(E e)метод за boolean add(Object o), който може да извършва каквато и да е обект на снимачната площадка, включително java.lang.Integerподтип java.lang.Object.)

Замърсяването на купчината може лесно да възникне в контекста на varargs. Например, помислете за списък 14.

Листинг 14. Демонстриране на замърсяване на купчини в контекст на varargs

 import java.util.Arrays; import java.util.List; public class UnsafeVarargsDemo { public static void main(String[] args) { unsafe(Arrays.asList("A", "B", "C"), Arrays.asList("D", "E", "F")); } static void unsafe(List... l) { Object[] oArray = l; oArray[0] = Arrays.asList(new Double(3.5)); String s = l[0].get(0); } } 

В Object[] oArray = l;заданието въвежда възможността за замърсяване на купчина. Стойност, която не съответства на параметризирания тип на параметъра varargs, lможе да бъде присвоена на променлива oArray. Компилаторът обаче не генерира непроверено предупреждение, защото вече го е направил при превод List... lна List[] l. Това присвояване е валидно, тъй като променливата lима типа List[]кой подтип Object[].

Също така компилаторът не издава предупреждение или грешка при присвояване на Listобект от всякакъв тип на някой от oArrayкомпонентите на масива на; например oArray[0] = Arrays.asList(new Double(3.5));,. Тази задача възлага на първия компонент спектър от oArrayедин Listобект, съдържащ един java.lang.Doubleобект.

На String s = l[0].get(0);задачата е проблематично. Обектът, съхраняван в първия компонент на масив от променлива, lима типа List, но това задание очаква обект от тип List. В резултат JVM хвърля java.lang.ClassCastException.

Компилирайте този изходен код ( javac -Xlint:unchecked UnsafeVarargsDemo.java). Трябва да наблюдавате следния изход (леко преформатиран за четливост), когато се компилира под Java SE 7 актуализация 6:

 UnsafeVarargsDemo.java:8: warning: [unchecked] unchecked generic array creation for varargs parameter of type List[] unsafe(Arrays.asList("A", "B", "C"), ^ UnsafeVarargsDemo.java:12: warning: [unchecked] Possible heap pollution from parameterized vararg type List static void unsafe(List... l) ^ 2 warnings 

В моето въведение за Java 101 към генеричните продукти заявих, че не можете да използвате параметри на типа в изрази за създаване на масиви. Например не можете да посочите elements = new E[size];. Когато се опитате да го направите, компилаторът отчита съобщение за "грешка при създаване на общ масив". Все пак е възможно да се създаде общ масив, но само в контекст на varargs и това е първото предупредително съобщение, което се отчита. Зад кулисите компилаторът се превръща List... lв List[] lи след това в List[] l.

Забележете, че предупреждението за замърсяване на купчина се генерира в unsafe()сайта за деклариране на метода. Това съобщение не се генерира на сайта за извикване на този метод, какъвто е случаят с Java 5 и 6 компилаторите.

Не всички методи на varargs ще допринесат за замърсяването на купчините. Обаче предупредително съобщение ще продължи да се издава на сайта за деклариране на метода. Ако знаете, че методът ви не допринася за замърсяването на купчината, можете да потиснете това предупреждение, като го декларирате с @SafeVarargsанотацията - Java 7 въведе типа java.lang.SafeVarargsанотация. Например, тъй като няма начин методът на Arraysкласа asList()да допринесе за замърсяването на купчината, декларацията на този метод е анотирана @SafeVarargs, както следва:

 @SafeVarargs public static  List asList(T... a) 

В @SafeVarargsанотацията елиминира създаването на родовия масив и куп предупредителни замърсяване съобщенията. Това е документирана част от договора на метода и твърди, че изпълнението на метода няма да обработва неправилно официалния параметър на varargs.

В заключение

Java 7 подобри производителността на разработчиците чрез въвеждане на автоматично управление на ресурсите чрез инструкцията try-with-resources заедно с нов AutoCloseableинтерфейс, превключване на низ, многократно приключване, окончателно пренасочване, двоични литерали, долни черти в числови литерали, промени в типа на компилатора алгоритъм за извод, който въведе т. нар. диамантен оператор и опростено извикване на метод на varargs. Следва в Java 101: Следващото поколение е поглед върху ламбда и функционалните функции на интерфейса на Java 8.

Тази история, "Java 101: Основната обиколка на езика Java, част 5", първоначално е публикувана от JavaWorld.