CodeGym /Java блог /Случаен /Разширяване и стесняване на референтни типове
John Squirrels
Ниво
San Francisco

Разширяване и стесняване на референтни типове

Публикувано в групата
здрасти В минал урок обсъдихме кастинг на примитивни типове. Да си припомним накратко за Howво стана дума. Разширяване и стесняване на референтни типове - 1Представихме си примитивни типове (в този случай числови типове) като кукли за гнездене, които варират по размер според количеството памет, което заемат. Както си спомняте, поставянето на по-малка кукла в по-голяма е лесно 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, която е по-ниско в дървото на наследяването. Логично е, че без изрична индикация компилаторът няма да позволи такава операция, но ако посочим типа в скоби, тогава всичко работи. Разширяване и стесняване на референтни типове - 2Помислете за друг по-интересен пример:

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();
Не можете да присвоите обект-предшественик на променлива-потомък. Можете да направите обратното.
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION