JavaRush /جاوا بلاگ /Random-UR /Расширение и сужение ссылочных типов

Расширение и сужение ссылочных типов

گروپ میں شائع ہوا۔
Hello! В одной из прошлых лекций мы обсуждали приведение примитивных типов. Давай вкратце вспомним, о чем шла речь. Расширение и сужение ссылочных типов - 1Мы представляли примитивные типы (в данном случае — числовые) в виде матрешек согласно объему памяти, которое они занимают. Как ты помнишь, поместить меньшую матрешку в большую будет просто How в реальной жизни, так и в программировании на Java.

public class Main {
   public static void main(String[] args) {
        short smallNumber = 100;
        int bigNumber =  smallNumber;
        System.out.println(bigNumber);
   }
}
Это пример автоматического преобразования, or расширения. Оно происходит само по себе, поэтому дополнительный code писать не нужно. В конце концов, мы не делаем ничего необычного: просто кладем матрешку поменьше в матрешку побольше. Другое дело, если мы попытаемся сделать наоборот и положить большую матрешку в меньшую. В жизни такое сделать нельзя, а в программировании можно. Но есть один нюанс. Если мы попытаемся положить meaning int в переменную short, у нас это так просто не выйдет. Ведь в переменную short поместится всего 16 бит информации, а meaning int занимает 32 бита! В результате передаваемое meaning исказится. Компилятор выдаст нам ошибку («чувак, ты делаешь что-то подозрительное!»), но если мы явно укажем, к Howому типу приводим наше meaning, он все-таки выполнит такую операцию.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
В примере выше мы так и поступor. Операция выполнена, но поскольку в переменную short поместилось только 16 бит из 32, итоговое meaning было искажено, и в результате мы получor число -27008. Такая операция называется явным преобразованием, or сужением.

Примеры расширения и сужения ссылочных типов

Now мы поговорим о тех же операциях, но применимо не к примитивным типам, а к an objectм и ссылочным переменным! Как же это работает в Java? На самом деле, довольно просто. Есть an objectы, которые не связаны между собой. Было бы логично предположить, что их нельзя преобразовать друг в друга ни явно, ни автоматически:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog();//error!

   }

}
Здесь мы, конечно, получим ошибку. Классы Cat и Dog между собой не связаны, и мы не написали «преобразователя» одних в других. Логично, что сделать это у нас не получится: компилятор понятия не имеет, How конвертировать эти an objectы между собой. Другое дело, если an objectы будут между собой связаны! Как? Прежде всего, с помощью наследования. Давай попробуем создать небольшую систему классов с наследованием. У нас будет общий класс, обозначающий животных:

public class Animal {
  
   public void introduce() {

       System.out.println("i'm Animal");
   }
}
Животные, How известно, бывают домашними и дикими:

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? Попробуй обосновать свой ответ, прежде чем продолжать чтение. А вот и результат! i'm Pet Почему ответ получился таким? Все просто. У нас есть переменная-родитель и an object-потомок. Написав:

Animal animal = new Pet();
мы произвели расширение ссылочного типа Pet и записали его an object в переменную Animal. Как и в случае с примитивными, расширение ссылочных типов в Java производится автоматически. Дополнительный code для этого писать не нужно. Теперь у нас к ссылке-родителю привязан an object-потомок, и в итоге мы видим, что вызов метода производится именно у класса-потомка. Если ты все еще не до конца понимаешь, почему такой code работает, перепиши его простым языком:

Животное животное = new ДомашнееЖивотное();
В этом же нет ниHowих проблем, правильно? Представь, что это реальная жизнь, а link в данном случае — простая бумажная бирка с надписью «Животное». Если ты возьмешь такую бумажку и прицепишь на ошейник любому домашнему животному, все будет в порядке. Любое домашнее животное все равно животное! Обратный процесс, то есть движение по дереву наследования вниз, к наследникам — это сужение:

public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Как видишь, здесь мы явно указываем к Howому классу хотим привести наш an object. Ранее у нас была переменная WildAnimal, а теперь Coyote, которая идет по дереву наследования ниже. Логично, что без явного указания компилятор такую операцию не пропустит, но если в скобках указать тип, все заработает. Расширение и сужение ссылочных типов - 2 Рассмотрим другой пример, поинтереснее:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal();//error!
   }
}
Компилятор выдает ошибку! В чем же причина? В том, что ты пытаешься присвоить переменной-потомку an object-родителя. Иными словами, ты хочешь сделать вот так:

ДомашнееЖивотное домашнееЖивотное = new Животное();
Но, может быть, если мы явно укажем тип, к которому пытаемся сделать приведение, у нас все получится? С числами вроде получилось, давай попробуем! :)

public class Main {

   public static void main(String[] args) {
      
       Pet pet = (Pet) new Animal();
   }
}
Exception in thread "main" java.lang.ClassCastException: Animal cannot be cast to Pet Ошибка! Компилятор в этот раз ругаться не стал, однако в результате мы получor исключение. Причина нам уже известна: мы пытаемся присвоить переменной-потомку an object-родителя. А почему, собственно, нельзя этого делать? Потому что не все Животные являются ДомашнимиЖивотными. Ты создал an object Animal и пытаешься присвоить его переменной Pet. Но, к примеру, койот тоже является Animal, но он не является Pet, домашним животным. Иными словами, когда ты пишешь:

Pet pet = (Pet) new Animal();
На месте new Animal() может быть Howое угодно животное, и совсем не обязательно домашнее! Естественно, твоя переменная Pet pet подходит только для хранения домашних животных (и их потомков), и не для всех подряд. Поэтому для таких случаев в Java было создано специальное исключение — ClassCastException, ошибка при приведении классов. Давай проговорим еще раз, чтобы было понятнее. Переменная(link)-родитель может указывать на an object класса-потомка:

public class Main {

   public static void main(String[] args) {

       Pet pet =  new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Например, здесь у нас проблем не возникнет. У нас есть an object Pet, на который указывает link Pet. Потом на этот же an object стала указывать новая link Animal. После чего мы делаем преобразование animal в Pet. Почему у нас это получилось, кстати? В прошлый раз мы получor исключение! Потому что в этот раз наш изначальный an object — Pet pet!

Pet pet =  new Pet();
А в прошлом примере это был an object Animal:

Pet pet = (Pet) new Animal();
Переменной-наследнику нельзя присвоить an object предка. Наоборот делать можно.
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION