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. Заключение
В този кратък урок видяхме приликите и разликите в и .
Макар и предимно сходни, между двете има тънки разлики по отношение на това дали те могат да бъдат преосмислени или не.