В чем разница между апкастингом и нисходящим кастингом относительно переменной класса

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

Например, в следующей программе класс 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();
    }
}

person Dhivakar    schedule 01.05.2014    source источник
comment
Dog - это Animal. В большинстве случаев в апкастинге нет необходимости, если вы не хотите использовать определенный перегруженный метод. callme существует как в Animal, так и в Dog. callme2 существует только в Dog, который вы преобразовываете a в Dog, чтобы он работал.   -  person Engineer2021    schedule 01.05.2014
comment
Что выводит ваш код?   -  person Malwinder Singh    schedule 28.04.2015
comment
Интересно то, что d.callme возвращает 'In callme of Dog', хотя d было преобразовано в животное !!   -  person Chris311    schedule 11.01.2016
comment
@ Chris311 и 'd', и 'a' указывают на один и тот же объект ... который является Dog, но 'a' имеет доступ только к специфическим методам Dog, когда он отключен во время выполнения. Фактически: Животное a = (Животное) d; не нужно, вам нужно только Animal a = d; как вы поднимаете вверх.   -  person Mark Keen    schedule 13.01.2016


Ответы (10)


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

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

Понижающее значение будет примерно таким:

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() или выполнив приведение вверх.

Чтобы вызвать метод подкласса, вы должны выполнить приведение вниз. Как показано выше, вы обычно рискуете 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 16, в которой введено сопоставление с образцом для instanceof:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog castedDog) {
    // now castedDog is available here as in the example above
}
person awksp    schedule 01.05.2014
comment
Правильное лимитирование гарантирует отсутствие ClassCastException или нет? Как в первом случае? - person Malwinder Singh; 28.04.2015
comment
@РС. Что вы имеете в виду под «правильным»? - person awksp; 29.04.2015
comment
@awksp Это отличный и четкий ответ. В значительной степени суммирует все, что мне нужно знать о кастинге. - person Gautham Honnavara; 16.11.2016
comment
Вы наверняка еще не делали классы, где животное - это собака? так почему ты вообще это проверяешь? - person barlop; 09.08.2017

Трансляция вниз и вверх была следующей:
 введите описание изображения здесь

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

Понижающее значение: когда мы хотим преобразовать суперкласс в подкласс, мы используем понижающее приведение (или сужение), а понижающее приведение невозможно напрямую в 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

Autoboxing-vs-Casting

person Premraj    schedule 21.01.2016
comment
Итак, нет такого способа повышения качества вызова родительского метода? - person karlihnos; 05.07.2017

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

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

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

Вы можете увидеть ниже пример того, как getType(); работает в зависимости от типа объекта (Dog, Pet, Police Dog).

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

  1. Собака - это суперкласс.

  2. Pet Dog - Собака расширяет Dog.

  3. Police Dog - Полицейская собака расширяет 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 по умолчанию виртуальные. Это означает, что любой метод может быть переопределен при использовании в наследовании, если этот метод не объявлен как final или static (объяснение относится к концепции виртуальных таблиц)

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

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(). Чтобы вызвать этот метод, который вам нужно уменьшить вручную, чтобы этот объект obj3 был PoliceDog

  ( (PoliceDog)obj3).secretID();

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

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

  ( (PetDog)obj2).dogName();

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

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

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

person Nagarjuna Yelisetty    schedule 27.09.2015
comment
«Полиморфизм использует автоматическое понижающее преобразование во время вызовов методов». Нет, это не так. Используемый механизм не указан, но самый обычный механизм - vtable - этого не делает. Посмотрите в объектном коде. Никакого уныния. - person user207421; 28.09.2015
comment
Почему нет? Это то, что происходит правильно, вы можете привести пример, когда это не сработает? - person Nagarjuna Yelisetty; 28.09.2015
comment
Почему нет? Это то, что происходит правильно ... вы можете привести пример, в котором утверждение «Полиморфизм использует автоматическое приведение вниз во время вызовов методов». потерпит неудачу или не будет правдой? - person Nagarjuna Yelisetty; 28.09.2015
comment
Это ваше мнение. Вы должны это доказать. Покажите, где в объектном коде происходит приведение вниз. Ответ на вопрос «почему бы и нет» - «потому что в этом нет необходимости». Vtable заботится об отправке метода, а переменная уже указывает на весь объект. - person user207421; 28.09.2015
comment
Насколько мне известно, мои утверждения верны, и это верно в каждом случае, я прошу вас привести пример, когда это не будет верным !! Вместо того, чтобы смело полагаться на документацию. И даже сообщество узнает, и я получу некоторые знания :) если это действительно не так И просто из энтузиазма я прокомментировал Почему нет? пожалуйста, не воспринимайте это негативно :) - person Nagarjuna Yelisetty; 28.09.2015
comment
«Насколько мне известно, мои утверждения верны и верны во всех случаях» не является доказательством. Это просто утверждение. Прошу вас подтвердить свои утверждения. Вы этого не делаете. По сути, вы просто повторяете себя. И я уже привел несколько опровержений. Я также предоставил процедуру принятия решения. Если вы можете найти в объектном коде при вызове метода отклонение, значит, вы правы, а я ошибаюсь. Так делается наука. Сделай это. И утверждение, что я «смело полагаюсь на документацию», является явным искажением фактов. Не делай этого. - person user207421; 28.09.2015
comment
Чтобы удовлетворить мое любопытство, какое именно «знание» заставило вас поверить в обратное? А что же плохого в том, чтобы полагаться на документацию? - person user207421; 28.09.2015
comment
1. Прошел через объектный код после вашего аргумента. 2. Все методы в java по умолчанию виртуальные. Это означает, что любой метод может быть переопределен при использовании в наследовании, если этот метод не объявлен как final или static. - person Nagarjuna Yelisetty; 28.09.2015

Я знаю, что этот вопрос задан довольно давно, но для новых пользователей этого вопроса. Пожалуйста, прочтите эту статью, в которой содержится полное описание восходящего, понижающего преобразования и использования оператора 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;
    }

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

person Nadeesha Thilakarathne    schedule 10.11.2015
comment
хороший момент: Mammal m = (Mammal) new Cat (); равно Mammal m = new Cat (); - person Catbuilts; 27.12.2016

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

/* 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();
    }
}
person UWAIS    schedule 19.09.2014
comment
а в последней строке это может быть: ((Dog) ref) .callme2 (); // для сужения / сужения и доступа к методу callme2 () класса Dog. - person udarH3; 04.05.2020

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

ПРЕДСТОЯНИЕ -> Скрытие

СКАЧАТЬ -> Раскрытие

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

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

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

person Alexis    schedule 29.08.2016

1.- Повышение качества.

Выполняя восходящее преобразование, вы определяете тег некоторого типа, который указывает на объект подтипа (тип и подтип могут называться классом и подклассом, если вам удобнее ...).

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();
        
    }

}
person Lemmy_Caution    schedule 06.10.2018

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

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

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

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

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

person amit shah    schedule 07.08.2015

восходящее преобразование означает преобразование объекта в супертип, в то время как нисходящее преобразование означает преобразование в подтип.

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

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

Animal a = (Animal)d;

or

Animal a = d;

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

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

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

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

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

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
person Rakesh    schedule 22.02.2016