CodeGym /Курсове /Java Core /Полиморфизъм и преодоляване

Полиморфизъм и преодоляване

Java Core
Ниво , Урок
На разположение

— Амиго, харесваш ли китове?

"Китове? Не, никога не съм чувал за тях."

„Това е като крава, само че е по-голямо и плува. Между другото, китовете са произлезли от крави. Ъъъ, or поне споделят общ прародител. Няма meaning.“

Полиморфизъм и преодоляване - 1

„Слушайте. Искам да ви разкажа за друг много мощен инструмент на ООП: полиморфизъм . Той има четири функции.“

1) Замяна на метода.

Представете си, че сте написали клас "Крава" за игра. Има много членски променливи и методи. Обектите от този клас могат да правят различни неща: да ходят, да ядат, да спят. Кравите също бият звънец, когато ходят. Да приемем, че сте внедрor всичко в класа до най-малкия детайл.

Полиморфизъм и преодоляване - 2

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

Започнахте да проектирате класа Whale и осъзнахте, че той е само малко по-различен от класа Cow. И двата класа използват много сходна логика и вие решавате да използвате наследяване.

Класът Cow е идеално подходящ да бъде родителски клас: той вече има всички необходими променливи и методи. Всичко, което трябва да направите, е да добавите способността на кита да плува. Но има проблем: вашият кит има крака, рога и камбана. В крайна сметка класът Cow реализира тази функционалност. Какво можеш да направиш?

Полиморфизъм и преодоляване - 3

Замяната на метод идва на помощ. Ако наследим метод, който не прави точно това, от което се нуждаем в нашия нов клас, можем да заменим метода с друг.

Полиморфизъм и преодоляване - 4

Как става това? В нашия наследствен клас ние декларираме метода, който искаме да променим (със същия подпис на метода като в родителския клас) . След това пишем нов code за метода. Това е. Сякаш старият метод на родителския клас не съществува.

Ето How работи:

Код Описание
class Cow
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
Тук дефинираме два класа:  Cow и  WhaleWhaleнаследява  Cow.

Класът  Whale замества  printName();метода.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Този code показва на екрана « Аз съм крава ».
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Този code показва на екрана « Аз съм кит ».

След като наследи Cowи замени printName, Whaleкласът всъщност има следните данни и методи:

Код Описание
class Whale
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Не знаем нищо за нито един стар метод.

— Честно казано, това очаквах.

2) Но това не е всичко.

„Да предположим, че  Cow класът има  printAllметод, който извиква другите два метода. Тогава codeът ще работи така:“

Екранът ще покаже:
Аз съм бял,
аз съм кит

Код Описание
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Екранът ще покаже:
Аз съм бял,
аз съм кит

Имайте предвид, че когато методът printAll () на класа Cow се извика на обект Whale, ще се използва методът printName() на Whale, а не този на Cow.

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

"Виждам."

„Можете да наследявате и отменяте само нестатичните методи. Статичните методи не се наследяват и следователно не могат да бъдат отменяни.“

Ето How изглежда класът Whale, след като приложим наследяване и заменим методите:

Код Описание
class Whale
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Ето How изглежда класът Whale, след като приложим наследяване и сменим метода. Не знаем нищо за нито един стар printNameметод.

3) Отливане на типа.

Ето още по-интересен момент. Тъй като един клас наследява всички методи и данни на своя родителски клас, обект от този клас може да бъде рефериран от променливи на родителския клас (и родителя на родителя и т.н., до класа Object). Помислете за този пример:

Код Описание
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printColor();
}
Екранът ще покаже:
Аз съм бял.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printColor();
}
Екранът ще покаже:
Аз съм бял.
public static void main(String[] args)
{
Object o = new Whale();
System.out.println(o.toString());
}
Екранът ще покаже:
Whale@da435a.
Методът toString() е наследен от класа Object.

„Хубави неща. Но защо ви трябва това?“

„Това е ценна функция. По-късно ще разберете, че е много, много ценна.“

4) Късно подвързване (динамично изпращане).

Ето How изглежда:

Код Описание
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Екранът ще покаже:
Аз съм кит.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printName();
}
Екранът ще покаже:
Аз съм кит.

Обърнете внимание, че не типът на променливата определя кой конкретен метод printName извикваме (този на класа Cow or Whale), а по-скоро типът на обекта, към който се отнася променливата.

Променливата Cow съхранява препратка към обект Whale и методът printName , дефиниран в класа Whale , ще бъде извикан.

— Е, не са добавor това за по-голяма яснота.

„Да, не е толкова очевидно. Запомнете това важно правило:“

Наборът от методи, които можете да извикате на променлива, се определя от типа на променливата. Но кой специфичен метод/имплементация се извиква се определя от типа/класа на обекта, към който се отнася променливата.

"Ще опитам."

„Ще се натъквате на това постоянно, така че бързо ще го разберете и никога няма да забравите.“

5) Отливане на типа.

Кастингът работи по различен начин за референтни типове, т.е. класове, отколкото за примитивни типове. Разширяващите и стесняващите преобразувания обаче се прилагат и за референтни типове. Помислете за този пример:

Разширяване на преобразуването Описание
Cow cow = new Whale();

Класическо разширяващо се преобразуване. Сега можете да извиквате само методи, дефинирани в класа Cow на обекта Whale.

Компилаторът ще ви позволи да използвате променливата cow само за извикване на онези методи, дефинирани от типа Cow.

Стесняване на преобразуването Описание
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
Класическо стесняващо преобразуване с проверка на типа. Променливата cow от тип Cow съхранява препратка към обект Whale.
Проверяваме дали това е така и след това извършваме (разширяващо) преобразуване на типа. Това се нарича още тип кастинг .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
Можете също така да извършите стесняващо преобразуване на референтен тип без проверка на типа на обекта.
В този случай, ако променливата крава сочи към нещо различно от обект Whale, ще бъде хвърлено изключение (InvalidClassCastException).

6) А сега нещо вкусно. Извикване на оригиналния метод.

Понякога, когато заменяте наследен метод, не искате да го замените изцяло. Понякога просто искате да добавите малко към него.

В този случай наистина искате codeът на новия метод да извика същия метод, но в базовия клас. И Java ви позволява да направите това. Ето How се прави  super.method():.

Ето няколко примера:

Код Описание
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.print("This is false: ");
super.printName();

System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Екранът ще покаже:
Аз съм бял.
Това е невярно: Аз съм крава,
аз съм кит

"Хм. Е, това беше няHowъв урок. Ушите ми на робота почти се стопиха."

„Да, това не са прости неща. Това е един от най-трудните материали, които ще срещнете. Професорът обеща да предостави връзки към материали от други автори, така че ако все още не разбирате нещо, можете да попълните пропуски."

Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION