— Амиго, харесваш ли китове?
"Китове? Не, никога не съм чувал за тях."
„Това е като крава, само че е по-голямо и плува. Между другото, китовете са произлезли от крави. Ъъъ, or поне споделят общ прародител. Няма meaning.“
„Слушайте. Искам да ви разкажа за друг много мощен инструмент на ООП: полиморфизъм . Той има четири функции.“
1) Замяна на метода.
Представете си, че сте написали клас "Крава" за игра. Има много членски променливи и методи. Обектите от този клас могат да правят различни неща: да ходят, да ядат, да спят. Кравите също бият звънец, когато ходят. Да приемем, че сте внедрor всичко в класа до най-малкия детайл.
Тогава изведнъж клиентът казва, че иска да пусне ново ниво на играта, където всички действия се развиват в морето, а главният герой е кит.
Започнахте да проектирате класа Whale и осъзнахте, че той е само малко по-различен от класа Cow. И двата класа използват много сходна логика и вие решавате да използвате наследяване.
Класът Cow е идеално подходящ да бъде родителски клас: той вече има всички необходими променливи и методи. Всичко, което трябва да направите, е да добавите способността на кита да плува. Но има проблем: вашият кит има крака, рога и камбана. В крайна сметка класът Cow реализира тази функционалност. Какво можеш да направиш?
Замяната на метод идва на помощ. Ако наследим метод, който не прави точно това, от което се нуждаем в нашия нов клас, можем да заменим метода с друг.
Как става това? В нашия наследствен клас ние декларираме метода, който искаме да променим (със същия подпис на метода като в родителския клас) . След това пишем нов code за метода. Това е. Сякаш старият метод на родителския клас не съществува.
Ето How работи:
Код | Описание |
---|---|
|
Тук дефинираме два класа: Cow и Whale . Whale наследява Cow .
Класът |
|
Този code показва на екрана « Аз съм крава ». |
|
Този code показва на екрана « Аз съм кит ». |
След като наследи Cow
и замени printName
, Whale
класът всъщност има следните данни и методи:
Код | Описание |
---|---|
|
Не знаем нищо за нито един стар метод. |
— Честно казано, това очаквах.
2) Но това не е всичко.
„Да предположим, че Cow
класът има printAll
метод, който извиква другите два метода. Тогава codeът ще работи така:“
Екранът ще покаже:
Аз съм бял,
аз съм кит
Код | Описание |
---|---|
|
|
|
Екранът ще покаже: Аз съм бял, аз съм кит |
Имайте предвид, че когато методът printAll () на класа Cow се извика на обект Whale, ще се използва методът printName() на Whale, а не този на Cow.
Важното е не класът, в който е написан методът, а по-скоро типът (класът) на обекта, на който се извиква методът.
"Виждам."
„Можете да наследявате и отменяте само нестатичните методи. Статичните методи не се наследяват и следователно не могат да бъдат отменяни.“
Ето How изглежда класът Whale, след като приложим наследяване и заменим методите:
Код | Описание |
---|---|
|
Ето How изглежда класът Whale, след като приложим наследяване и сменим метода. Не знаем нищо за нито един стар printName метод. |
3) Отливане на типа.
Ето още по-интересен момент. Тъй като един клас наследява всички методи и данни на своя родителски клас, обект от този клас може да бъде рефериран от променливи на родителския клас (и родителя на родителя и т.н., до класа Object). Помислете за този пример:
Код | Описание |
---|---|
|
Екранът ще покаже: Аз съм бял. |
|
Екранът ще покаже: Аз съм бял. |
|
Екранът ще покаже: Whale@da435a. Методът toString() е наследен от класа Object. |
„Хубави неща. Но защо ви трябва това?“
„Това е ценна функция. По-късно ще разберете, че е много, много ценна.“
4) Късно подвързване (динамично изпращане).
Ето How изглежда:
Код | Описание |
---|---|
|
Екранът ще покаже: Аз съм кит. |
|
Екранът ще покаже: Аз съм кит. |
Обърнете внимание, че не типът на променливата определя кой конкретен метод printName извикваме (този на класа Cow or Whale), а по-скоро типът на обекта, към който се отнася променливата.
Променливата Cow съхранява препратка към обект Whale и методът printName , дефиниран в класа Whale , ще бъде извикан.
— Е, не са добавor това за по-голяма яснота.
„Да, не е толкова очевидно. Запомнете това важно правило:“
Наборът от методи, които можете да извикате на променлива, се определя от типа на променливата. Но кой специфичен метод/имплементация се извиква се определя от типа/класа на обекта, към който се отнася променливата.
"Ще опитам."
„Ще се натъквате на това постоянно, така че бързо ще го разберете и никога няма да забравите.“
5) Отливане на типа.
Кастингът работи по различен начин за референтни типове, т.е. класове, отколкото за примитивни типове. Разширяващите и стесняващите преобразувания обаче се прилагат и за референтни типове. Помислете за този пример:
Разширяване на преобразуването | Описание |
---|---|
|
Класическо разширяващо се преобразуване. Сега можете да извиквате само методи, дефинирани в класа Cow на обекта Whale. Компилаторът ще ви позволи да използвате променливата cow само за извикване на онези методи, дефинирани от типа Cow. |
Стесняване на преобразуването | Описание |
---|---|
|
Класическо стесняващо преобразуване с проверка на типа. Променливата cow от тип Cow съхранява препратка към обект Whale. Проверяваме дали това е така и след това извършваме (разширяващо) преобразуване на типа. Това се нарича още тип кастинг . |
|
Можете също така да извършите стесняващо преобразуване на референтен тип без проверка на типа на обекта. В този случай, ако променливата крава сочи към нещо различно от обект Whale, ще бъде хвърлено изключение (InvalidClassCastException). |
6) А сега нещо вкусно. Извикване на оригиналния метод.
Понякога, когато заменяте наследен метод, не искате да го замените изцяло. Понякога просто искате да добавите малко към него.
В този случай наистина искате codeът на новия метод да извика същия метод, но в базовия клас. И Java ви позволява да направите това. Ето How се прави super.method()
:.
Ето няколко примера:
Код | Описание |
---|---|
|
|
|
Екранът ще покаже: Аз съм бял. Това е невярно: Аз съм крава, аз съм кит |
"Хм. Е, това беше няHowъв урок. Ушите ми на робота почти се стопиха."
„Да, това не са прости неща. Това е един от най-трудните материали, които ще срещнете. Професорът обеща да предостави връзки към материали от други автори, така че ако все още не разбирате нещо, можете да попълните пропуски."
GO TO FULL VERSION