— Амиго, харесваш ли китове?
"Китове? Не, никога не съм чувал за тях."
„Това е като крава, само че е по-голямо и плува. Между другото, китовете са произлезли от крави. Ъъъ, 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ъв урок. Ушите ми на робота почти се стопиха."
„Да, това не са прости неща. Това е един от най-трудните материали, които ще срещнете. Професорът обеща да предостави връзки към материали от други автори, така че ако все още не разбирате нещо, можете да попълните пропуски."