1. Всички класове наследяватObject
Всички класове в Java имплицитно наследяват Object
класа.
Ще анализираме Howво е наследяването и How работи в Java в търсенето на Java Core. Засега ще разгледаме един прост факт, който следва от това:
Обект от всеки клас може да бъде присвоен на Object
променлива. Пример:
Код | Забележка |
---|---|
|
Променливата o съхранява препратка към Scanner обект |
|
Променливата o съхранява препратка към String обект |
|
Променливата o съхранява препратка към Integer обект |
|
Променливата o съхранява препратка към String обект |
Тук добрите новини свършват. Компилаторът не следи оригиналния тип обект, записан в Object
променлива, така че няма да можете да извиквате методи на записания обект , различни от методите на Object
класа.
Ако трябва да извикате методите, свързани с оригиналния тип на обекта, тогава трябва първо да запишете препратка към него в променлива от правилния тип и след това да извикате методите на тази променлива:
Код | Забележка |
---|---|
|
Програмата няма да се компorра. Класът Object няма nextInt() метод. |
|
Това ще свърши работа. Тук запазваме препратка към Scanner обект в Scanner променлива, използвайки оператор за придаване на тип . |
Не можете просто да отидете и да присвоите Object
променлива на променлива на Scanner, дори ако Object
променливата съхранява препратка към Scanner
обект. Но можете да направите това, ако използвате оператора typecast , за който вече знаете. Това е общият му вид:
Type name1 = (Type) name2;
Къде name1
е името на Type
променлива и name2
е името на Object
променлива, която съхранява препратка към Type
обект.
Преобразуване на типове
Ако типът на променливата и типът на обекта не съвпадат, тогава ClassCastException
ще бъде хвърлено a. Пример:
Код | Забележка |
---|---|
|
Ще възникне грешка по време на изпълнение: тук ще бъде изведено a ClassCastException |
Има начин да избегнете тази грешка в Java: правим това, като проверяваме типа на обекта, съхранен в променлива :
name instanceof Type
Операторът instanceof
проверява дали name
променливата е Type
обект.
Като пример, нека намерим низ в масив от различни обекти:
Код | Забележка |
---|---|
|
Autoboxing ще преобразува тези стойности съответно в Integer , String и Double . Цикъл върху масива от обекти Ако обектът е String Запазете го в String променлива Показване на променливата на екрана. |
2. Защо се появиха генериците — колекции
Да се върнем към колекциите.
Веднага след като Java разработчиците създадоха ArrayList
класа, те искаха да го направят универсален, така че да може да съхранява всяHowъв тип обект. Така че те използваха масив от Object
s за съхраняване на елементите.
Силата на този подход е, че можете да добавите обект от всяHowъв тип към колекцията.
Разбира се, има няколко слабости.
Недостатък 1.
Винаги е било необходимо да се напише оператор за преобразуване на тип, когато се извличат елементи от колекция:
Код | Забележка |
---|---|
|
Създайте колекция за съхраняване на препратки към Object обекти . Попълнете колекцията с числа 10 , 20 , ... 100 ; Обобщете елементите на колекцията. Необходимо е преобразуване на типове |
Недостатък 2.
Нямаше гаранция, че колекцията съдържа определен тип елемент
Код | Забележка |
---|---|
|
Създайте колекция за съхраняване на препратки към Object обекти Ние запълваме колекцията с числа, представени като Double обекти: 0.0 , 2.5 , 5.0 , ... Сумирайте елементите на колекцията Ще има грешка: a Double не може да бъде преобразувано вInteger |
Данните могат да бъдат поставени в колекцията навсякъде:
- в друг метод
- в друга програма
- от файл
- по мрежата
Недостатък 3.
Данните в колекцията могат да бъдат променени случайно.
Можете да предадете колекция, пълна с вашите данни, към няHowъв метод. Този метод, написан от друг програмист, добавя своите данни към вашата колекция.
Името на колекцията не показва ясно кои типове данни могат да се съхраняват в нея. И дори ако дадете ясно име на вашата променлива, препратка към нея може да бъде предадена на дузина методи и тези методи определено няма да знаят нищо за оригиналното име на променливата.
3. Генерични лекарства
В Java всички тези проблеми се елиминират от това страхотно нещо, наречено генерични.
В Java генеричните средства означават възможността за добавяне на параметри на типове към типове. Резултатът е сложен композитен тип. Общият изглед на такъв съставен тип е следният:
ClassName<TypeParameter>
Това е общ клас. И може да се използва навсякъде, където обикновено използвате класове.
Код | Описание |
---|---|
|
Създаване на променливи |
|
Създаване на обекти |
|
Създаване на масиви |
Integer
В такава колекция могат да се съхраняват само променливи:
Код | Описание |
---|---|
|
ArrayList колекция с Integer елементи Това е разрешено И това също ще работи
Автобокс
Но това не е позволено: грешка при компorране |
Ще научите How да създавате свои собствени класове с параметри на типа в търсенето на Java Collections. Засега ще разгледаме How да ги използваме и How работят.
4. Как работят генеричните лекарства
Всъщност генериците са ужасно примитивни.
Компилаторът просто заменя генеричните типове с обикновени типове. Но когато се използват методи от общ тип, компилаторът добавя оператор за преобразуване на типа за преобразуване на параметри към параметрите на типа:
Код | Какво прави компилаторът |
---|---|
|
|
|
|
|
|
|
|
Да предположим, че имаме метод, който сумира числата в колекция от цели числа:
Код | Какво прави компилаторът |
---|---|
|
|
С други думи, генериците са вид синтактична захар, точно като autoboxing, но малко повече. С autoboxing компилаторът добавя методи за преобразуване на an int
в an Integer
и обратно, а за генеричните добавя оператори за придаване на тип.
След като компилаторът компorра вашите генерични класове с параметри на типа, те просто се преобразуват в обикновени класове и оператори за преобразуване на типа. Информацията за аргументите на типа, предадени на променливи от общи типове, се губи. Този ефект се нарича още изтриване на типа .
Понякога програмистите, които пишат генерични класове (класове с параметри на типа), наистина се нуждаят от информацията за типовете, предадени като аргументи. В търсенето на Java Collections ще научите How да се справите с това и Howво включва то.
5. Няколко факта за генериците
Ето още няколко интересни факта за генериците.
Класовете могат да имат няколко параметъра на типа. Изглежда нещо подобно:
ClassName<TypeParameter1, TypeParameter2, TypeParameter3>
Всъщност това не е изненадващо. Навсякъде, където компилаторът може да добави оператор за преобразуване към един тип, той може да добави множество оператори за преобразуване на типа.
Примери:
Код | Забележка |
---|---|
|
Първият put параметър на метода е a Integer , а вторият е aString |
Генеричните типове също могат да се използват като параметри . Изглежда нещо подобно:
ClassName<TypeParameter<TypeParameterParameter>>
Да предположим, че искаме да създадем списък, който ще съхранява списъци с низове. В този случай ще получим нещо подобно:
// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");
// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");
// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);
Генеричните типове (типове с параметри на типа) също могат да се използват като типове масиви. Изглежда нещо подобно:
ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];
Тук не се случва нищо магическо: ъгловите скоби просто показват името на типа:
Код | Негенеричен аналог |
---|---|
|
|
|
|
|
|
GO TO FULL VERSION