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


143

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

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

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

Каков результат вашего кода?
Малвиндер Сингх

Интересно то, что d.callme возвращает 'In callme of Dog', хотя d было преобразовано в животное !!
Chris311

4
@ Chris311 и 'd', и 'a' указывают на один и тот же объект ... который является Dog, но 'a' имеет доступ только к специфическим методам Dog, когда он отключен во время выполнения. Фактически: Животное a = (Животное) d; не нужно, вам нужно только Animal a = d; как вы поднимаете вверх.
Марк Кин,

Ответы:


226

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

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

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

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

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

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

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

Вы получите файл ClassCastException. Причина в том, что animalтип среды выполнения равен Animal, и поэтому, когда вы говорите среде выполнения выполнить приведение, она видит, что на animalсамом деле это не a, Dogи поэтому выдает a 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;
}

Правильное лимитирование гарантирует «нет» ClassCastExceptionили нет? Как в первом случае?
Малвиндер Сингх

@MS Что вы имеете в виду под «правильным»?
awksp

2
@awksp Это отличный и четкий ответ. В значительной степени суммирует все, что мне нужно знать о кастинге.
Gautham Honnavara

Вы наверняка еще не делали классы, где животное - это собака? так почему ты вообще его проверяешь?
barlop 09

65

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

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

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

Итак, нет такого способа повышения качества для вызова родительского метода?
karlihnos 05

33

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

  ( (PetDog)obj2).dogName();

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

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

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


«Полиморфизм использует автоматическое понижающее преобразование во время вызовов методов». Нет, это не так. Используемый механизм не указан, но самый обычный механизм - vtable - этого не делает. Посмотрите в объектном коде. Никакого уныния.
Маркиз Лорн

Почему нет? Вот что происходит правильно, можете ли вы привести пример, когда это не сработает?
Нагарджуна Йелисетти

1
Почему нет? Это то, что происходит правильно ... вы можете привести пример, в котором утверждение «Полиморфизм использует автоматическое приведение вниз во время вызовов методов». потерпит неудачу или не будет правдой?
Нагарджуна Елисетти,

Это ваше мнение. Вы должны это доказать. Покажите, где в объектном коде происходит приведение вниз. Ответ на вопрос «почему бы и нет» - «потому что в этом нет необходимости». Vtable заботится об отправке метода, а переменная уже указывает на весь объект.
Маркиз Лорн

1
«Насколько мне известно, мои утверждения верны и верны в любом случае» не является доказательством. Это просто утверждение. Я прошу вас , чтобы доказать свои утверждения. Вы этого не делаете. По сути, вы просто повторяете себя. И я уже привел несколько опровержений. Я также предоставил процедуру принятия решения. Если вы можете найти в объектном коде при вызове метода отклонение, вы правы, а я ошибаюсь. Так делается наука. Сделай это. И утверждение, что я «смело полагаюсь на документацию», является явным искажением фактов. Не делай этого.
Marquis of Lorne

12

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

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


хороший момент: Mammal m = (Mammal) new Cat (); равно Млекопитающее m = new Cat ();
Catbuilts

6

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

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

а в последней строке это может быть: ((Dog) ref) .callme2 (); // для сужения / сужения и доступа к методу callme2 () класса Dog.
udarH3

6

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

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

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

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

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

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


4

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

}

2

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

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

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

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

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


1

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

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

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

Animal a = (Animal)d;

или

Animal a = d;

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

Вместо этого необходимо понижение, потому что вы определили aкак объект Animal. В настоящее время вы знаете, что это a 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");
    }
    ... 
} 

0

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

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.