"Здрасти, Амиго! Темата на днешния урок е разширяване и стесняване на преобразуването на типове. Научи за разширяване и стесняване на примитивни типове преди много време. На ниво 10. Днес ще говорим за това How работи за референтни типове, т.е. екземпляри на класове."

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

Ето един пример:

Код Описание
class Animal
{
public void doAnimalActions();
}class Cat extends Animal
{
public void doCatActions();
}class Tiger extends Cat
{
public void doTigerActions();
}
Тук имаме три декларации за клас: Animal, Cat и Tiger. Котката наследява Животното. И Тигър наследява Котка.
public static void main(String[] args)
{
Tiger tiger = new Tiger();
Cat cat = new Tiger();
Animal animal = new Tiger();
Object obj = new Tiger();
}
Обект Tiger винаги може да бъде присвоен на променлива, чийто тип е този на един от неговите предци. За клас Тигър това са котка, животно и предмет.

Сега нека да разгледаме разширяването и стесняването на реализациите.

Ако операция по присвояване ни накара да се придвижим нагоре по веригата на наследяване (към класа Object), тогава имаме работа с разширяващо се преобразуване (известно също като upcasting). Ако се придвижим надолу по веригата към типа на обекта, тогава това е стесняващо преобразуване (известно също като низходящо преобразуване).

Придвижването нагоре по веригата на наследяване се нарича разширяване, защото води до по-общ тип. По този начин обаче губим способността да извикваме методите, добавени към класа чрез наследяване.

Код Описание
public static void main(String[] args)
{
Object obj = new Tiger();
Animal animal = (Animal) obj;
Cat cat = (Cat) obj;
Tiger tiger = (Tiger) animal;
Tiger tiger2 = (Tiger) cat;
}
Когато стеснявате типа, трябва да използвате оператор за преобразуване на типа, т.е. извършваме явно преобразуване.

Това кара Java машината да провери дали обектът наистина наследява типа, в който искаме да го конвертираме.

Тази малка иновация доведе до многократно намаляване на броя на грешките при преобразуване на типове и значително увеличи стабилността на Java програмите.

Код Описание
public static void main(String[] args)
{
Object obj = new Tiger();
if (obj instanceof Cat)
{
Cat cat = (Cat) obj;
cat.doCatActions();
}}
Още по-добре използвайте  instanceof check
public static void main(String[] args)
{
Animal animal = new Tiger();
doAllAction(animal);

Animal animal2 = new Cat();
doAllAction(animal2);

Animal animal3 = new Animal();
doAllAction(animal3);
}

public static void doAllAction(Animal animal)
{
if (animal instanceof Tiger)
{
Tiger tiger = (Tiger) animal;
tiger.doTigerActions();
}

if (animal instanceof Cat)
{
Cat cat = (Cat) animal;
cat.doCatActions();
}

animal.doAnimalActions();
}
И ето защо. Разгледайте примера вляво.

Ние (нашият code) не винаги знаем с Howъв тип обект работим. Може да бъде обект от същия тип като променливата (животно) or произволен тип потомък (котка, тигър).

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

С други думи, работи правилно и за трите типа: Животно, Котка и Тигър.

public static void main(String[] args)
{
Cat cat = new Tiger();
Animal animal = cat;
Object obj = cat;
}
Тук имаме три операции за присвояване. Всички те са примери за разширяване на реализациите.

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

„О, предпоследният пример изясни всичко: защо е необходима проверката и защо е необходимо преобразуване на типа.“

„Надявам се. Искам да обърна внимание на този факт:“

Нищо от това не кара обекта да се променя по ниHowъв начин! Единственото нещо, което се променя, е броят на наличните методи за извикване на определена референтна променлива.

Например, променлива Cat ви позволява да извиквате методите doAnimalActions и doCatActions. Той не знае нищо за метода doTigerActions, дори и да сочи към Tiger обект.

„Да, разбирам. Беше по-лесно, отколкото си мислех, че ще бъде.“