1. Перетворення типів

Змінні вказівних типів (класів) теж можна перетворювати на різні типи. Однак це діє лише в межах однієї ієрархії типів. Розгляньмо простий приклад. Припустімо, у нас є така ієрархія класів: класи, показані нижче, успадковуються від класів, показаних вище.

Перетворення типів

Перетворення вказівних типів, так само як і примітивних, поділяються на розширення й звуження.

Ми бачимо, що клас Кіт успадковано від класу ДомашняТварина, а клас ДомашняТварина своєю чергою успадковано від класу Тварина.

Напишімо такий код:

Тварина котик = new Кіт();

Це перетворення є розширенням типу: його ще називають неявним. Ми розширили посилання котик, і тепер воно посилається на об'єкт типу Кіт. У разі такого перетворення ми не зможемо через посилання котик викликати методи, які має клас Кіт, але яких не має клас Тварина.

Перетворення-звуження (або явне перетворення) відбувається у зворотному напрямку:

Кіт киця = (Кіт) котик;

Ми явно вказали, що хочемо перетворити посилання, що зберігається у змінній котик (типу Тварина), на тип Кіт.



2. Перевірка типу об'єкта

Але тут слід бути дуже обережними. Напишімо такий код:

Тварина звір = new Кіт ();
Вовк сірийВовк = (Вовк) звір;

Компілятор цей код пропустить, але під час виконання програми станеться помилка! JVM кине виняток:

Exception in thread "main" java.lang.ClassCastException: Кіт cannot be cast to Вовк

Посилання на об'єкт Кіт можна присвоїти тільки змінним, які мають тип батьківського класу для класу Кіт: ДомашняТварина, Тварина і Object.

Чому це так?

Річ у тім, що посилання на об'єкт використовується для того, щоб звертатися до методів і змінних цього об'єкта. Якщо зберегти у змінній типу Тварина посилання на об'єкт Кіт, то ніяких проблем не буде: тип Кіт завжди матиме змінну й методи типу Тварина, бо ж він їх успадкував!

Проте якби JVM дозволила зберегти посилання на об'єкт Кіт у змінній типу Вовк, могла б виникнути ситуація, коли для змінної сірийВовк викликано метод, відсутній в об'єкта Кіт, на який ця змінна посилається. Тому такі збереження заборонені.

У Java є спеціальний оператор instanceof, який дає змогу перевірити, чи можна зберегти об'єкт певного типу в змінній певного типу. Він має досить простий вигляд:

змінна instanceof Тип

Приклад:

Тварина звір = new Кіт ();
if (звір instanceof Вовк)
{
   Вовк сірийВовк = (Вовк) звір;
}

Такий код не спричинить помилок навіть під час виконання.

От іще кілька прикладів з описом ситуації:

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

Класичне розширення типу — оператор перетворення типу не потрібен. Тепер для об'єкта типу Whale можна викликати тільки методи, описані в класі Cow.

Компілятор дозволить викликати для змінної cow тільки методи її типу — класу Cow.

Звуження типу
Cow cow = new Whale();
if (cow instanceof Whale)
{
   Whale whale = (Whale) cow;
}
Класичне звуження типу: потрібно додати перевірку типу й оператор перетворення типу.
Змінна cow типу Cow зберігає посилання на об'єкт класу Whale.
Перевіряємо, що це насправді так, а потім виконуємо операцію перетворення (звуження) типу. Або як її ще називають —
type cast.

Cow cow = new Cow();
Whale whale = (Whale) cow; // exception
Вказівне звуження типу можна виконати навіть без перевірки типу об'єкта.
Причому, якщо у змінній cow зберігався об'єкт, який не належить до класу Whale, буде згенеровано виняток InvalidClassCastException.


3. Виклик оригінального методу: super

Іноді під час перевизначення потрібно лише трохи доповнити метод батьківського класу, а не замінити його на свій повністю.

Було б класно, якби під час виклику свого методу можна було викликати такий самий метод батьківського класу, а потім виконати ще й свій код. Або спочатку виконати свій код, а потім викликати метод батьківського класу.

І така можливість у Java є. Код для виклику методу саме батьківського класу має такий вигляд:

super.метод(параметри);

Приклади:

class МирнийЧас
{
   public double getPi()
   {
      return 3.14;
   }
}

class ВоєннийЧас extends МирнийЧас
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

У воєнний час число Pi може досягати значення 4, а в нашому випадку навіть 6! Це, звісно, жарт, але він демонструє, як усе це працює.

От іще кілька прикладів для кращого розуміння:

Код Опис
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("Я — білий");
   }

   public void printName()
   {
      System.out.println("Я — корова");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("Це неправда: ");
      super.printName();
      System.out.println("Я — кит");
   }
}
Класи Cow і Whale
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
На екран буде виведено напис:
Я — білий
Це неправда: Я — корова
Я — кит

Це дійсно непростий матеріал — щиро кажучи, одна з найскладніших тем в ООП. Проте його потрібно знати й розуміти.