В чем разница между повышением и понижением относительно переменной класса

В чем разница между повышением и понижением относительно переменной класса?

Например, в следующей программе класс Animal содержит только один метод, а класс Dog содержит два метода, а затем способ преобразования переменной Dog в переменную Animal.

Если приведение завершено, то как мы можем вызвать другой метод Dog с переменной Animal.

class Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}


class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class UseAnimlas 
{
    public static void main (String [] args) 
    {
        Dog d = new Dog();      
        Animal a = (Animal)d;
        d.callme();
        a.callme();
        ((Dog) a).callme2();
    }
}

10 ответов

Решение

Upcasting - это приведение к супертипу, а downcasting - к подтипу. Upcasting всегда разрешен, но downcasting включает в себя проверку типа и может бросить ClassCastException,

В вашем случае, бросок из Dog для Animal это уныние, потому что Dog это Animal, В общем, вы можете использовать upcast, когда есть отношения между двумя классами.

Даункинг будет примерно таким:

Animal animal = new Dog();
Dog castedDog = (Dog) animal;

По сути, вы говорите компилятору, что вы знаете, какой тип объекта у него на самом деле. Компилятор разрешит преобразование, но все равно вставит проверку работоспособности во время выполнения, чтобы убедиться, что преобразование имеет смысл. В этом случае приведение возможно, потому что во время выполнения animal на самом деле Dog хотя статический тип animal является Animal,

Однако, если вы должны были сделать это:

Animal animal = new Animal();
Dog notADog = (Dog) animal;

Вы бы получили ClassCastException, Причина в том, что animalТип времени выполнения Animalи поэтому, когда вы указываете среде выполнения выполнить приведение, он видит, что animal на самом деле не Dog и так бросает ClassCastException,

Для вызова метода суперкласса вы можете сделать super.method() или выполняя upcast.

Чтобы вызвать метод подкласса, вы должны сделать downcast. Как показано выше, вы обычно рискуете ClassCastException делая это; Тем не менее, вы можете использовать instanceof оператор проверяет тип времени выполнения объекта перед выполнением приведения, что позволяет предотвратить ClassCastExceptions:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog) {
    // Guaranteed to succeed, barring classloader shenanigans
    Dog castedDog = (Dog) animal;
}

Преобразование и преобразование было следующим:
введите описание изображения здесь

Апкастинг: Когда мы хотим привести Подкласс к Суперклассу, мы используем Апкастинг (или расширение). Это происходит автоматически, не нужно ничего делать явно.

Даункастинг: когда мы хотим преобразовать суперкласс в субкласс, мы используем даункастинг (или сужение), и даункастинг напрямую не возможен в Java, что явно необходимо сделать.

Dog d = new Dog();
Animal a = (Animal) d; //Explicitly you have done upcasting. Actually no need, we can directly type cast like Animal a = d; compiler now treat Dog as Animal but still it is Dog even after upcasting
d.callme();
a.callme(); // It calls Dog's method even though we use Animal reference.
((Dog) a).callme2(); // Downcasting: Compiler does know Animal it is, In order to use Dog methods, we have to do typecast explicitly.
// Internally if it is not a Dog object it throws ClassCastException

Upcasting и downcasting являются важной частью Java, которая позволяет нам создавать сложные программы с использованием простого синтаксиса и дает нам большие преимущества, такие как полиморфизм или группирование различных объектов. Java позволяет обрабатывать объект типа подкласса как объект любого типа суперкласса. Это называется апкастинг. Обновление выполняется автоматически, в то время как преобразование должно выполняться вручную программистом, и я постараюсь объяснить, почему это так.

Upcasting и downcasting НЕ похожи на приведение примитивов от одного к другому, и я считаю, что это вызывает много путаницы, когда программист начинает изучать приведение объектов.

Полиморфизм: все методы в Java являются виртуальными по умолчанию. Это означает, что любой метод может быть переопределен при использовании в наследовании, если только этот метод не объявлен как final или static.

Вы можете увидеть пример ниже, как getType(); работает в соответствии с типом объекта (собака, животное, полицейская собака).

Предположим, у вас есть три собаки

  1. Собака - это супер класс.
  2. Pet Dog - Pet Dog расширяет собаку.
  3. Полицейская собака - Полицейская собака расширяет Pet Dog.

    public class Dog{ 
       public String getType () {
          System.out.println("NormalDog");
          return "NormalDog";
       }
     }
    
    /**
     * Pet Dog has an extra method dogName()
     */   
    public class PetDog extends Dog{ 
       public String getType () {
          System.out.println("PetDog");
          return "PetDog";
       }
       public String dogName () {
          System.out.println("I don't have Name !!");
          return "NO Name";
       }
     }
    
    /**
     * Police Dog has an extra method secretId()
     */
    public class PoliceDog extends PetDog{
    
     public String secretId() {
        System.out.println("ID");
        return "ID";
     }
    
     public String getType () {
         System.out.println("I am a Police Dog");
         return "Police Dog";
     }
    }
    

Полиморфизм: все методы в Java являются виртуальными по умолчанию. Это означает, что любой метод может быть переопределен при использовании в наследовании, если только этот метод не объявлен как окончательный или статический (объяснение относится к концепции виртуальных таблиц)

Виртуальная таблица / Таблица отправки: Таблица отправки объекта будет содержать адреса динамически связанных методов объекта. Вызовы метода выполняются путем извлечения адреса метода из таблицы диспетчеризации объекта. Таблица диспетчеризации одинакова для всех объектов, принадлежащих к одному и тому же классу, и, следовательно, обычно распределяется между ними.

public static void main (String[] args) {
      /**
       * Creating the different objects with super class Reference
       */
     Dog obj1 = new Dog();
`         /**
           *  Object of Pet Dog is created with Dog Reference since                
           *  Upcasting is done automatically for us we don't have to worry about it 
           *  
           */
     Dog obj2 = new PetDog();
`         /**
           *  Object of Police Dog is created with Dog Reference since                
           *  Upcasting is done automatically for us we don't have to worry       
           *  about it here even though we are extending PoliceDog with PetDog 
           *  since PetDog is extending Dog Java automatically upcast for us 
           */
      Dog obj3 = new PoliceDog();
}



 obj1.getType();

Печать Normal Dog

  obj2.getType();

Печать Pet Dog

 obj3.getType();

Печать Police Dog

Даункинг должен быть сделан программистом вручную

Когда вы пытаетесь вызвать secretID(); метод на obj3 который PoliceDog object но ссылаются на Dog который является суперклассом в иерархии, он выдает ошибку, так как obj3 не имеют доступа к secretId() метод. Для того, чтобы вызвать этот метод вам нужно PoliceDog

  ( (PoliceDog)obj3).secretID();

который печатает ID

Аналогичным образом, чтобы вызвать dogName(); метод в PetDog класс вам нужно убить obj2 в PetDog поскольку obj2 ссылается на Dog и не имеют доступа к dogName(); метод

  ( (PetDog)obj2).dogName();

Почему это так, что трансляция происходит автоматически, а трансляция должна быть ручной? Ну, видишь ли, апскейтинг никогда не подведет. Но если у вас есть группа разных собак и вы хотите понизить их всех до их типов, то есть шанс, что некоторые из этих собак на самом деле разных типов, т.е. PetDog, PoliceDog и процесс завершается с ошибкой, бросая ClassCastException,

Это причина, по которой вам нужно вручную понижать ваши объекты, если вы ссылались на тип суперкласса.

Примечание: здесь ссылка означает, что вы не изменяете адрес памяти ваших объектов, когда вы понижаете его, он все равно остается тем же, что вы просто группируете их для определенного типа в этом случае Dog

Я знаю этот вопрос, заданный довольно давно, но для новых пользователей этого вопроса. Пожалуйста, прочитайте эту статью, где содержится полное описание по апскейтингу, аппроксимированию и использованию оператора instanceof

  • Нет необходимости выгружать вручную, это происходит само по себе:

    Mammal m = (Mammal)new Cat(); равно Mammal m = new Cat();

  • Но уныние всегда должно быть сделано вручную:

    Cat c1 = new Cat();      
    Animal a = c1;      //automatic upcasting to Animal
    Cat c2 = (Cat) a;    //manual downcasting back to a Cat
    

Почему это так, что трансляция происходит автоматически, а трансляция должна быть ручной? Ну, видишь ли, апскейтинг никогда не подведет. Но если у вас есть группа разных животных и вы хотите превратить их всех в кошку, то есть шанс, что некоторые из этих животных на самом деле являются собаками, и процесс завершится неудачей, вызвав исключение ClassCastException. Именно здесь следует ввести полезную функцию под названием "instanceof", которая проверяет, является ли объект экземпляром какого-либо класса.

 Cat c1 = new Cat();         
    Animal a = c1;       //upcasting to Animal
    if(a instanceof Cat){ // testing if the Animal is a Cat
        System.out.println("It's a Cat! Now i can safely downcast it to a Cat, without a fear of failure.");        
        Cat c2 = (Cat)a;
    }

Для получения дополнительной информации, пожалуйста, прочитайте эту статью

Лучше попробуй этот метод для апскейтинга, это легко понять:

/* upcasting problem */
class Animal
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}

class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class Useanimlas 
{
    public static void main (String [] args) 
    {
        Animal animal = new Animal ();
        Dog dog = new Dog();
        Animal ref;
        ref = animal;
        ref.callme();
        ref = dog;
        ref.callme();
    }
}

Может быть, эта таблица помогает. Вызов callme() метод класса Parent или класс Child, Как принцип:

ОБКАЧКА -> Сокрытие

СКАЧАТЬ -> Выявление

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

1.- Обновление.

Делая upcasting, вы определяете тег некоторого типа, который указывает на объект подтипа (Тип и подтип можно назвать классом и подклассом, если вы чувствуете себя более комфортно...).

Animal animalCat = new Cat();

Что означает, что такой тег animalCat будет иметь функциональность (методы) только типа Animal, потому что мы объявили его как тип Animal, а не как тип Cat.

Нам разрешено делать это "естественным / неявным / автоматическим" способом, во время компиляции или во время выполнения, главным образом потому, что Cat наследует некоторые из своих функций от Animal; например, move(). (По крайней мере, кот это животное, не так ли?)

2.- Даункинг.

Но что произойдет, если нам потребуется получить функциональность Cat из нашего тега Animal типа?.

Поскольку мы создали тег animalCat, указывающий на объект Cat, нам нужен способ вызывать методы объекта Cat из нашего тега animalCat довольно умным способом.

Такую процедуру мы называем Downcasting, и мы можем сделать это только во время выполнения.

Время для некоторого кода:

public class Animal {
    public String move() {
        return "Going to somewhere";
    }
}

public class Cat extends Animal{
    public String makeNoise() {
        return "Meow!";
    }   
}

public class Test {

    public static void main(String[] args) {

    //1.- Upcasting 
    //  __Type_____tag________object
        Animal animalCat = new Cat();
    //Some animal movement
        System.out.println(animalCat.move());
        //prints "Going to somewhere"

    //2.- Downcasting   
    //Now you wanna make some Animal noise.
        //First of all: type Animal hasn't any makeNoise() functionality.
        //But Cat can do it!. I wanna be an Animal Cat now!!

        //___________________Downcast__tag_____ Cat's method
        String animalNoise = ( (Cat) animalCat ).makeNoise();

        System.out.println(animalNoise);
        //Prints "Meow!", as cats usually done.

    //3.- An Animal may be a Cat, but a Dog or a Rhinoceros too.
        //All of them have their own noises and own functionalities.
        //Uncomment below and read the error in the console:

    //  __Type_____tag________object
        //Cat catAnimal = new Animal();

    }

}

Родитель: Автомобиль
Ребенок: Фигу
Автомобиль с1 = новый Фигу ();

=====
Приведение к базовому типу:-
Метод: Объект c1 будет ссылаться на методы класса (Figo - метод должен быть переопределен), потому что класс "Figo" указан как "new".
Переменная экземпляра: объект c1 будет ссылаться на переменную экземпляра класса объявления ("Автомобиль").

Когда класс объявления является родительским, а объект создается дочерним, происходит неявное приведение типа "Upcasting".

======
Понижающее приведение:-
Фигура f1 = (Фигура) c1; //
Метод: Объект f1 будет ссылаться на метод класса (фиг.1C), так как исходный объект c1 создается с помощью класса "Figo". но после того, как приведение вниз выполнено, методы, которые присутствуют только в классе "Figo", также могут быть отнесены к переменной f1.
Переменная экземпляра: объект f1 не будет ссылаться на переменную экземпляра класса объявления объекта c1 (класс объявления для c1 равен CAR), но при понижении он будет ссылаться на переменные экземпляра класса Figo.

======
Использование: Когда объект имеет дочерний класс, а класс объявления - родительский, а дочерний класс хочет получить доступ к переменной Instance своего собственного класса, а не родительского класса, тогда это можно сделать с помощью "Downcasting".

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

В java апкастинг не нужен, так как это делается автоматически. И это обычно называют неявным приведением. Вы можете указать это, чтобы сделать это понятным для других.

Таким образом, написание

Animal a = (Animal)d;

или же

Animal a = d;

приводит к одной и той же точке, и в обоих случаях будут выполнены callme() от Dog,

Даункастинг необходим вместо того, чтобы вы a как объект животного. В настоящее время вы знаете, что это Dog, но у Java нет никаких гарантий. На самом деле во время выполнения он может быть другим, и Java будет бросать ClassCastExceptionЭто случится. Конечно, это не тот случай, к которому вы привели пример. Если вы не сыграете a в AnimalЯва не могла даже скомпилировать приложение, потому что Animal не имеет метода callme2(),

В вашем примере вы не можете получить код callme() из Animal от UseAnimlas (так как Dog перезаписать его), если метод не будет следующим:

class Dog extends Animal 
{ 
    public void callme()
    {
        super.callme();
        System.out.println("In callme of Dog");
    }
    ... 
} 

Мы можем создать объект для Downcasting. В этом типе также.: вызов методов базового класса

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
Другие вопросы по тегам