Java 101: Przewodnik po podstawowych funkcjach języka Java, część 5

Poprzednia 1 2 Strona 2 Strona 2 z 2

Wnioskowanie o typie i konstruktory generyczne dla klas ogólnych i nieogólnych

Klasy ogólne i nieogólne mogą deklarować konstruktory ogólne, w których konstruktor ma listę parametrów typu formalnego. Na przykład możesz zadeklarować następującą klasę generyczną z konstruktorem generycznym:

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

Ta deklaracja określa klasę ogólną Boxz parametrem typu formalnego E. Określa również konstruktora generycznego z parametrem typu formalnego T. Możesz utworzyć wystąpienie klasy generycznej i wywołać jej konstruktor w następujący sposób:

 new Box("Aggies") 

To wyrażenie tworzy instancję Box, przechodząc Marbledo E. Ponadto kompilator wnioskuje Stringjako Targument typu rzeczywistego, ponieważ argument konstruktora jest Stringobiektem.

Kompilatory pre-Java 7 wnioskują o rzeczywistych argumentach typu konstruktora generycznego podobnie do argumentów metody ogólnej. Jednak kompilator Java 7 może wywnioskować rzeczywiste argumenty typu klasy generycznej, której instancja jest tworzona w kontekście operatora diamentu. Rozważmy następujący przykład:

 Box box = new Box("Aggies"); 

Oprócz wnioskowania o typie Marbledla parametru typu formalnego Eklasy ogólnej Box, kompilator wnioskuje o typ Stringdla parametru typu formalnego konstruktora Ttej klasy ogólnej.

Mała zmiana w Project Coin # 8: Uproszczone wywołanie metody varargs

Przed wersją Java 7 każda próba wywołania metody varargs (argumenty zmiennych, znanej również jako zmienna arity ) z typem zmiennym varargs, którego nie można reifidować, powodowała, że ​​kompilator wyświetlał ostrzeżenie o „niebezpiecznej operacji”. Aby wyeliminować możliwość pojawienia się wielu podobnych komunikatów ostrzegawczych (po jednym na stronę wywołania), Java 7 przeniosła ostrzeżenie z witryny wywołania do deklaracji metody.

Typy reifable i non-reifable

Reifiable typ odsłania swoją pełną informację o typie w czasie wykonywania. Przykłady obejmują typy pierwotne, typy nieogólne, typy surowe i wywołania niezwiązanych symboli wieloznacznych. W przeciwieństwie do tego, którego nie można reifiable , informacje o typie są usuwane w czasie kompilacji przez wymazanie typu, aby zapewnić zgodność binarną z bibliotekami Java i aplikacjami, które zostały utworzone przed typami generycznymi. Przykłady obejmują Seti Set. Ponieważ typ nienaprawialny nie jest w pełni dostępny w czasie wykonywania, maszyna JVM nie może odróżnić od Seti Set; w czasie wykonywania Setdostępny jest tylko typ surowy .

Metody ogólne, które zawierają parametry wejściowe vararg, mogą powodować zanieczyszczenie sterty , w którym zmienna sparametryzowanego typu odwołuje się do obiektu, który nie jest tego typu sparametryzowanego (na przykład jeśli typ surowy został zmieszany z typem sparametryzowanym). Kompilator zgłasza „niezaznaczone ostrzeżenie”, ponieważ nie można zweryfikować poprawności operacji obejmującej sparametryzowany typ (np. Rzutowanie lub wywołanie metody).

Listing 13 przedstawia zanieczyszczenie sterty w kontekście innym niż varargs.

Listing 13. Demonstrowanie zanieczyszczenia hałdy w kontekście innym niż varargs

 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); } } } 

Zmienna ssma sparametryzowany typ Set. Gdy element java.util.Set, do którego odwołuje się, sjest przypisany ss, kompilator generuje niezaznaczone ostrzeżenie. Dzieje się tak, ponieważ kompilator nie może określić, że sodnosi się do Settypu (tak nie jest). Rezultatem jest zanieczyszczenie sterty. (Kompilator zezwala na to przypisanie, aby zachować kompatybilność wsteczną ze starszymi wersjami Java, które nie obsługują typów ogólnych. Ponadto wymazywanie typów przekształca się Setw Set, co powoduje Setprzypisanie jednego do drugiego Set).

Kompilator generuje ostrzeżenie drugiego niezaznaczone na linii, która wywołuje Set„s add()metody. Dzieje się tak, ponieważ nie może określić, czy zmienna sodnosi się do typu Setlub Set. To kolejna sytuacja zanieczyszczenia sterty. (Kompilator umożliwia tej metody połączenia, ponieważ Erasure przekształca Set„y boolean add(E e)Sposób się boolean add(Object o), co może dodawać żadnych przedmiotów do zestawu, w tym java.lang.Integerpodtypów java.lang.Object.)

Zanieczyszczenie hałdy może łatwo wystąpić w kontekście varargs. Rozważmy na przykład Listing 14.

Listing 14. Demonstrowanie zanieczyszczenia hałdy w kontekście 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;Przyporządkowanie wprowadza możliwość zanieczyszczenia sterty. Wartość niezgodną ze sparametryzowanym typem parametru varargs lmożna przypisać do zmiennej oArray. Jednak kompilator nie generuje niezaznaczonego ostrzeżenia, ponieważ już to zrobił podczas tłumaczenia List... lna List[] l. To przypisanie jest prawidłowe, ponieważ zmienna lma typ List[], który podtypy Object[].

Ponadto kompilator nie wyświetla ostrzeżenia ani błędu podczas przypisywania Listobiektu dowolnego typu do któregokolwiek ze oArrayskładników tablicy; na przykład oArray[0] = Arrays.asList(new Double(3.5));. To zadanie nadaje się do pierwszego składnika matrycy oArrayna Listobiekty zawierające pojedynczy java.lang.Doubleprzedmiot.

To String s = l[0].get(0);zadanie jest problematyczne. Obiekt przechowywany w pierwszym składniku tablicy zmiennej lma typ List, ale to przypisanie wymaga obiektu typu List. W rezultacie JVM rzuca java.lang.ClassCastException.

Skompiluj ten kod źródłowy ( javac -Xlint:unchecked UnsafeVarargsDemo.java). Podczas kompilacji w środowisku Java SE 7 z aktualizacją 6 należy zwrócić uwagę na następujące dane wyjściowe (nieco przeformatowane w celu zwiększenia czytelności):

 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 

We wprowadzeniu do języka generycznego w języku Java 101 stwierdziłem, że nie można używać parametrów typu w wyrażeniach tworzących tablice. Na przykład nie możesz określić elements = new E[size];. Kompilator zgłasza komunikat „ogólny błąd tworzenia tablicy”, gdy próbujesz to zrobić. Jednak nadal możliwe jest utworzenie ogólnej tablicy, ale tylko w kontekście varargs, i to jest właśnie to, co zgłasza pierwszy komunikat ostrzegawczy. W tle kompilator przekształca się List... ldo, List[] la następnie do List[] l.

Zauważ, że ostrzeżenie o zanieczyszczeniu sterty jest generowane w unsafe()miejscu deklaracji metody. Ten komunikat nie jest generowany w witrynie wywołań tej metody, co ma miejsce w przypadku kompilatorów Java 5 i 6.

Nie wszystkie metody Varargs przyczynią się do zanieczyszczenia hałdy. Jednak komunikat ostrzegawczy będzie nadal wyświetlany w miejscu deklaracji metody. Jeśli wiesz, że Twoja metoda nie przyczynia się do zanieczyszczenia sterty, możesz pominąć to ostrzeżenie, deklarując je za pomocą @SafeVarargsadnotacji - Java 7 wprowadziła java.lang.SafeVarargstyp adnotacji. Na przykład, ponieważ nie ma sposobu, aby metoda Arraysklasy asList()przyczyniła się do zanieczyszczenia sterty, deklaracja tej metody została opatrzona adnotacją @SafeVarargsw następujący sposób:

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

@SafeVarargsAdnotacja eliminuje rodzajowe tworzenia tablicy i sterty komunikaty ostrzegawcze zanieczyszczeń. Jest to udokumentowana część kontraktu metody i zapewnia, że ​​implementacja metody nie będzie nieprawidłowo obsługiwać formalnego parametru varargs.

Podsumowując

Java 7 poprawiła produktywność programistów, wprowadzając automatyczne zarządzanie zasobami za pomocą instrukcji try-with-resources wraz z nowym AutoCloseableinterfejsem, ciągiem włączania, wielokrotnym przechwytywaniem, ostatecznym powtórzeniem, literałami binarnymi, podkreśleniami w literałach numerycznych, zmianami typu kompilatora algorytm wnioskowania, który wprowadził tzw. operator diamentu i uprościł wywołanie metody varargs. Następny krok w Java 101: Seria następnej generacji to spojrzenie na lambdę i funkcjonalne funkcje języka interfejsu Java 8.

Artykuł „Java 101: Przewodnik po podstawowych funkcjach języka Java, część 5” został pierwotnie opublikowany przez JavaWorld.