здрасти В минал урок обсъдихме кастинг на примитивни типове. Да си припомним накратко за Howво стана дума.
Представихме си примитивни типове (в този случай числови типове) като кукли за гнездене, които варират по размер според количеството памет, което заемат. Както си спомняте, поставянето на по-малка кукла в по-голяма е лесно Howто в реалния живот, така и при програмирането на Java.
Помислете за друг по-интересен пример:

public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
short smallNumber = (short) bigNumber;
System.out.println(smallNumber);
}
}
Това е пример за автоматично преобразуване or разширяване . Това се случва от само себе си, така че не е необходимо да пишете допълнителен code. В крайна сметка ние не правим нищо необичайно: просто поставяме по-малка кукла в по-голяма кукла. Друг е въпросът, ако се опитаме да направим обратното и да поставим по-голяма руска кукла в по-малка кукла. Не можете да направите това в реалния живот, но в програмирането можете. Но има един нюанс. Ако се опитаме да поставим int
в short
променлива, нещата не вървят толкова гладко за нас. В крайна сметка променливата short
съдържа само 16 бита информация, но int
заема 32 бита! В резултат на това предадената стойност е изкривена. Компилаторът ще ни даде грешка (' Пич, правиш нещо подозрително!'). Но ако изрично посочим типа, към който преобразуваме нашата стойност, той ще продължи и ще изпълни операцията.
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
bigNumber = (short) bigNumber;
System.out.println(bigNumber);
}
}
Точно това направихме в примера по-горе. Операцията е извършена, но тъй като short
променливата може да побере само 16 от 32-та byteа, крайната стойност е изкривена и получаваме числото -27008 . Такава операция се нарича явно преобразуване or стесняване .
Примери за разширяване и стесняване на референтни типове
Сега нека поговорим за същите оператори, приложени не към примитивни типове, а към обекти и референтни променливи ! Как работи това в Java? Всъщност е доста просто. Има обекти, които не са свързани. Би било логично да се предположи, че те не могат да бъдат преобразувани един в друг, нито изрично, нито автоматично:
public class Cat {
}
public class Dog {
}
public class Main {
public static void main(String[] args) {
Cat cat = new Dog(); // Error!
}
}
Тук, разбира се, получаваме грешка. Класовете Cat
и Dog
не са свързани помежду си и не сме написали „конвертор“, който да се премества от един към друг. Логично е, че не можем да направим това: компилаторът няма представа How да преобразува тези обекти от един тип в друг. Ако обектите са свързани, това е друг въпрос! Свързано How? Преди всичко по наследство. Нека опитаме да използваме наследяването, за да създадем малка система от класове. Ще имаме общ клас за представяне на животни:
public class Animal {
public void introduce() {
System.out.println("I'm Animal");
}
}
Всеки знае, че животните могат да бъдат опитомени (домашни любимци) or диви:
public class WildAnimal extends Animal {
public void introduce() {
System.out.println("I'm WildAnimal");
}
}
public class Pet extends Animal {
public void introduce() {
System.out.println("I'm Pet");
}
}
Например, вземете кучета - имаме домашни кучета и койоти:
public class Dog extends Pet {
public void introduce() {
System.out.println("I'm Dog");
}
}
public class Coyote extends WildAnimal {
public void introduce() {
System.out.println ("I'm Coyote");
}
}
Избрахме специално най-основните класове, за да ги направим по-лесни за разбиране. Всъщност не се нуждаем от полета и един метод е достатъчен. Нека се опитаме да изпълним този code:
public class Main {
public static void main(String[] args) {
Animal animal = new Pet();
animal.introduce();
}
}
Какво мислите, че ще се показва на конзолата? Ще бъде ли извикан introduce
методът на Pet
класа or класът? Animal
Опитайте се да аргументирате отговора си, преди да продължите да четете. И ето го резултата! Аз съм домашен любимец Защо получихме това? Всичко е просто. Имаме родителска променлива и наследствен обект. чрез писане,
Animal animal = new Pet();
разширихме препраткаPet
и я присвоихме на Animal
променлива . Както при примитивните типове, референтните типове се разширяват автоматично в Java. Не е необходимо да пишете допълнителен code, за да го направите. Сега имаме обект-потомък, присвоен на родителска препратка. В резултат на това виждаме, че извикването на метода се извършва в класа потомък. Ако все още не разбирате напълно защо този code работи, пренапишете го на обикновен език:
Animal animal = new DomesticatedAnimal();
Няма проблем с това, нали? Представете си, че това е реалният живот и препратката е просто хартиен етикет с надпис „Животно“. Ако вземете това парче хартия и го прикрепите към нашийника на всеки домашен любимец, всичко ще бъде правилно. В крайна сметка всеки домашен любимец е животно! Обратният процес - придвижване надолу по дървото на наследяването към потомците - се стеснява:
public class Main {
public static void main(String[] args) {
WildAnimal wildAnimal = new Coyote();
Coyote coyote = (Coyote) wildAnimal;
coyote.introduce();
}
}
Както можете да видите, тук ясно посочваме класа, към който искаме да конвертираме нашия обект. Преди имахме WildAnimal
променлива, а сега имаме Coyote
, която е по-ниско в дървото на наследяването. Логично е, че без изрична индикация компилаторът няма да позволи такава операция, но ако посочим типа в скоби, тогава всичко работи. 
public class Main {
public static void main(String[] args) {
Pet pet = new Animal(); // Error!
}
}
Компилаторът генерира грешка! Но защо? Тъй като се опитвате да присвоите родителски обект на препратка към наследник. С други думи, вие се опитвате да направите нещо подобно:
DomesticatedAnimal domesticatedAnimal = new Animal();
Е, може би всичко ще работи, ако изрично посочим типа, към който се опитваме да конвертираме? Това проработи с числа — нека опитаме! :)
public class Main {
public static void main(String[] args) {
Pet pet = (Pet) new Animal();
}
}
Изключение в нишката "main" java.lang.ClassCastException: Animal не може да бъде прехвърлено към Pet Error! Компилаторът не ни извика този път, но в крайна сметка получихме изключение. Вече знаем причината: опитваме се да присвоим родителски обект на препратка към наследник. Но защо точно не можете да направите това? Защото не всички животни са опитомени. Създадохте Animal
обект и се опитвате да го присвоите на Pet
променлива. Койотът също е Animal
, но не е Pet
. С други думи, когато пишете
Pet pet = (Pet) new Animal();
new Animal()
може да представлява всяко животно, не непременно домашен любимец! Естествено, вашата Pet pet
променлива е подходяща само за съхранение на домашни любимци (и техните потомци), а не за всяHowъв вид животни. Ето защо беше създадено специално Java изключение, ClassCastException
, за случаите, когато възникне грешка при кастинг на класове. Нека го прегледаме отново, за да направим нещата по-ясни. Родителска препратка може да сочи към екземпляри на клас потомък:
public class Main {
public static void main(String[] args) {
Pet pet = new Pet();
Animal animal = pet;
Pet pet2 = (Pet) animal;
pet2.introduce();
}
}
Тук например нямаме проблеми. Имаме Pet
обект, към който се отнася Pet
променлива. По-късно Animal
препратка сочи към същия обект. След това преобразуваме animal
в Pet
. Между другото, защо това ни подейства? Последният път направихме изключение! Защото този път оригиналният ни обект е Pet
!
Pet pet = new Pet();
Но в последния пример това беше Animal
обект:
Pet pet = (Pet) new Animal();
Не можете да присвоите обект-предшественик на променлива-потомък. Можете да направите обратното.
GO TO FULL VERSION