1. Преобразуване на типове

Променливите, които съхраняват референтни типове (класове), също могат да бъдат конвертирани в различни типове. Но това работи само в йерархия от един тип. Нека да разгледаме един прост пример. Да предположим, че имаме следната йерархия на класовете, в която класовете по-долу наследяват класовете по-горе.

Преобразуване на типове

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

Виждаме, че класът Cat наследява класа Pet, а класът Pet на свой ред наследява класа Animal.

Ако напишем code по този начин:

Animal kitten = new Cat();

Това е преобразуване на разширяващ се тип . Нарича се още имплицитно предаване. Разширихме препратката към котката , така че вече се отнася до обект Cat . С подобно преобразуване на типове няма да можем да използваме препратката към котето за извикване на методи, които присъстват в класа Cat , но липсват в класа Animal .

Стесняващо преобразуване (or изрично предаване) се случва в обратната посока:

Cat cat = (Cat) kitten;

Ние изрично посочихме, че искаме да прехвърлим препратката, съхранена в променливата kitten (чийто тип е Animal ) към типа Cat .



2. Проверка на типа на обект

Но тук трябва да сте много внимателни. Ако направите това:

Animal beast = new Cat();
Wolf grayWolf = (Wolf) beast;

Компилаторът ще позволи този code, но ще има грешка, когато програмата се изпълнява! JVM ще хвърли изключение:

Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to a Wolf

Препратките към обект Cat могат да се съхраняват само в променливи, чийто тип е предшественик на класа Cat: Pet, Animal or Object.

Защо така?

Съответният момент тук е, че препратката към обект се използва за препратка към методите и променливите на този обект . И няма да има проблеми, ако използваме променлива Animal, за да съхраним препратка към обект Cat: типът Cat винаги има променливи и методи от типа Animal — той ги е наследил!

Но ако JVM ни позволи да съхраним препратка към обект Cat в променлива Wolf, тогава може да имаме ситуация, в която може да се опитаме да използваме променливата grayWolf , за да извикаме метод, който не съществува в обекта Cat, съхранен в тази променлива . Ето защо тази подредба не е разрешена.

Java има специален instanceofоператор, който ви позволява да проверите дали даден обект е от определен тип и следователно може да бъде съхранен в променлива от определен тип. Изглежда доста просто:

variable instanceof Type

Пример:

Animal beast = new Cat();
if (beast instanceof Wolf)
{
   Wolf grayWolf = (Wolf) beast;
}

Този code няма да причини грешки — дори по време на изпълнение.

Ето още няколко примера, които илюстрират ситуацията:

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

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

На cowпроменливата компилаторът ще ви позволи да извиквате само методи, които Cowима нейният тип (класът).

Преобразуване на стесняващ тип
Cow cow = new Whale();
if (cow instanceof Whale)
{
   Whale whale = (Whale) cow;
}
Класическо стесняващо преобразуване: Трябва да добавите проверка на типа и оператор за предаване.
Променливата Cow cowсъхранява препратка към Whaleобект.
Проверяваме дали това е така и след това извършваме (стесняващо) преобразуване на типа. Или Howто още се нарича:
тип отливка
.

Cow cow = new Cow();
Whale whale = (Whale) cow; // Exception
Можете да стесните референтен тип, без да проверявате типа на обекта.
Ако cowпроменливата се отнася до обект, който не е Whale, тогава InvalidClassCastExceptionще бъде генериран.


3. Извикване на оригиналния метод: superключовата дума

Когато заместваме метода на родителския клас, понякога instead of да го заменим с наш собствен, искаме само леко да го допълним.

Би било страхотно, ако можем да използваме метода на родителския клас в нашия метод и след това да изпълним част от нашия собствен code. Или може би първо да изпълним нашия собствен code и след това да извикаме метода на родителския клас.

И Java ни позволява точно това. За да извикате метод на родителския клас, направете следното:

super.method(arguments);

Примери:

class PeaceTime
{
   public double getPi()
   {
      return 3.14;
   }
}

class WarTime extends PeaceTime
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

Във военно време стойността на Piможе да бъде по-голяма от 6! Разбира се, ние се шегуваме, но този пример показва How всичко това може да работи.

Ето още няколко примера, за да изясним малко нещата:

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

   public void printColor()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a cow");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("This is incorrect: ");
      super.printName();
      System.out.println("I'm a whale");
   }
}
Cowи Whaleкласове
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
Изходът на екрана ще бъде:
I'm a white whale
This is incorrect: I'm a cow
I'm a whale

Това са трудни неща. Честно казано, това е едно от най-трудните неща в OOP . Въпреки това трябва да го знаете и разбирате.