Java Generics - срещу

1. Общ преглед

В този бърз урок ще видим приликите и разликите между и в Java Generics .

Въпреки това, тъй като това е напреднала тема, наложително е да разберете основно темата, преди да се потопим в същността на въпроса.

2. История на генеричните лекарства

В JDK 5 бяха въведени генерични лекарства, за да се премахнат грешките по време на компилация и да се засили безопасността на типа. Тази допълнителна безопасност на типа елиминира кастинга в някои случаи на употреба и дава възможност на програмистите да пишат общи алгоритми, като и двете могат да доведат до по-четлив код.

Например, преди JDK 5, ще трябва да работим с елементите на списък, като използваме кастинг. Това от своя страна създаде определен клас грешки по време на изпълнение:

List aList = new ArrayList(); aList.add(new Integer(1)); aList.add("a_string"); for (int i = 0; i < aList.size(); i++) { Integer x = (Integer) aList.get(i); }

Сега този код има два проблема, които бихме искали да разгледаме:

  • Нуждаем се от изричен глас, за да извлечем стойности от aList - типът зависи от типа променлива вляво - Integer в този случай
  • Ще получим грешка по време на изпълнение при втората итерация, когато се опитваме да хвърлим a_string към цяло число

Дженериците изпълняват ролята за нас:

List iList = new ArrayList(); iList.add(1); iList.add("a_string"); // compile time error for (int i = 0; i < iList.size(); i++) { int x = iList.get(i); } 

Компилаторът ще ни каже, че не е възможно да добавите a_string към списък от тип Integer , което е по-добре, отколкото да откриете по време на изпълнение.

Освен това не е необходимо изрично кастинг, тъй като компилаторът вече знае, че iList държи Integer s. Освен това, поради магията на разопаковането, дори не ни беше необходим тип Integer , неговата примитивна форма е достатъчна.

3. заместващи символи в генеричните продукти

Въпросният знак или заместващ символ се използва в генеричните продукти, за да представи неизвестен тип. Той може да има три форми:

  • Неограничени заместващи символи : Списъкът представлява списък с неизвестен тип
  • Горни ограничени заместващи символи : Списъкът представлява списък с Number или неговите подтипове, като Integer и Double
  • Долни ограничени заместващи символи : Списъкът представлява списък с цяло число или неговите супертипове Number и Object

Сега, тъй като Object е присъщият супертип на всички типове в Java, бихме се изкушили да мислим, че той също може да представлява неизвестен тип. С други думи, Списъкът и Списъкът могат да служат на една и съща цел. Но те не го правят.

Нека разгледаме тези два метода:

public static void printListObject(List list) {     for (Object element : list) {         System.out.print(element + " ");     }         }     public static void printListWildCard(List list) {     for (Object element: list) {         System.out.print(element + " ");     }     } 

Като се има предвид списък на Integer s, кажете:

List li = Arrays.asList(1, 2, 3);

printListObject (li) няма да се компилира и ще получим тази грешка:

The method printListObject(List) is not applicable for the arguments (List)

Докато printListWildCard (li) ще се компилира и ще изведе 1 2 3 към конзолата.

4. и - приликите

В горния пример, ако променим подписа на метода за printListWildCard на:

public static void printListWildCard(List list)

Той ще функционира по същия начин, както printListWildCard (списък със списък) . Това се дължи на факта, че Object е супертип на всички Java обекти и основно всичко разширява Object . И така, списъкът на целите числа също се обработва.

Накратко, означава ли това ? и ? extends Object са синоними в този пример .

Макар че в повечето случаи това би било вярно, но има и няколко разлики . Нека ги разгледаме в следващия раздел.

5. и - разликата

Reifiable типове са тези, чийто тип не се изтрива по време на компилация. С други думи, представянето по време на изпълнение на нерефибируем тип ще има по-малко информация от неговия аналог по време на компилация, защото част от него ще бъде изтрита.

Като общо правило параметризираните типове не могат да бъдат преосмислени. Това означава, че списъкът и картата не могат да бъдат преосмислени. Компилаторът изтрива типа им и ги третира съответно като Списък и Карта .

Единственото изключение от това правило са неограничените типове заместващи символи. Това означава, Списък и карта са reifiable .

От друга страна, Списъкът не може да се преоборудва . Макар и фина, това е забележителна разлика.

Нерефибируемите типове не могат да се използват в определени ситуации, като например в оператор на instanceof или като елементи на масив.

Така че, ако напишем:

List someList = new ArrayList(); boolean instanceTest = someList instanceof List

Този код се компилира и instanceTest е вярно .

Но ако използваме оператора instanceof в Списък :

List anotherList = new ArrayList(); boolean instanceTest = anotherList instanceof List;

тогава ред 2 не се компилира.

По същия начин, в долния фрагмент, ред 1 се компилира, но ред 2 не:

List[] arrayOfList = new List[1]; List[] arrayOfAnotherList = new List[1]

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

В този кратък урок видяхме приликите и разликите в и .

Макар и предимно сходни, между двете има тънки разлики по отношение на това дали те могат да бъдат преосмислени или не.