Яка різниця між оновленнями вгору та знищенням по відношенню до змінної класу


138

Яка різниця між оновленням вгору та знищенням по відношенню до змінної класу?

Наприклад, у наступному програмному класі Animal міститься лише один метод, але клас Dog містить два способи, то як ми переведемо змінну 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();
    }
}

А Dog- це Animal. Більшість випадків оновлення не потрібні, якщо ви не хочете використовувати певний метод перевантаження. callmeіснує в обох Animalі Dog. callme2існує лише в Dog, на яке ти кидаєш, aщоб Dogвін працював.
Брайан

Який вихід вашого коду?
Мальфіндер Сінгх

Цікавим є те, що d.callme повертає "In callme of Dog", хоча d був закинутий твариною !!
Chris311

4
@ Chris311 і "d", і "a" вказують на один і той же об'єкт ... який є собакою, але "a" має доступ лише до певних методів для собаки, коли вона знищена під час виконання. Факт: тварина a = (тварина) d; зайвий, вам потрібна лише тварина a = d; як ти поквапився.
Марк Кін

Відповіді:


223

Оновлення - це кастинг до супертипу, а мовлення - до підтипу. Оновлення завжди дозволено, однак передача даних передбачає перевірку типу і може кинути ClassCastException.

У вашому випадку команда від a Dogдо an Animalє оновленням, тому що Dogє-a Animal. Взагалі, ви можете похитнюватися, коли є стосунки між двома класами.

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

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

В основному то , що ви робите , це говорить компілятору , що ви знаєте , що тип виконання об'єкта на насправді є. Компілятор дозволить перетворити, але все одно буде вставляти перевірку правильності виконання, щоб переконатися, що перетворення має сенс. У цьому випадку, амплітуда можлива, оскільки час виконання animalфактично є Dogрівним, хоча статичний тип animalє Animal.

Однак якщо ви це зробили:

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

Ви отримаєте ClassCastException. Причина тому, що animalтип "runtime" є Animal, і тоді, коли ви повідомляєте час виконання виконувати анонси, він бачить, що animalце насправді не так 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 Це відмінна і чітка відповідь. Досить багато резюмує все, що потрібно знати про кастинг.
Готем Хонавара

Ви точно не проводили занять, де тварина є екземпляром собаки? так чому ви це навіть перевіряєте?
барлоп

62

Лиття вниз і лиття було таким:
введіть тут опис зображення

Оновлення : Коли ми хочемо передати підклас до класу Super, ми використовуємо Upcasting (або розширення). Це відбувається автоматично, не потрібно нічого чітко робити.

Downcasting : Коли ми хочемо привласнити клас Super до 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

32

Оновлення та посилання вниз є важливою частиною Java, яка дозволяє нам будувати складні програми, використовуючи простий синтаксис, і дає нам великі переваги, наприклад, поліморфізм або групування різних об'єктів. Java дозволяє об'єкту типу підкласу трактуватись як об'єкт будь-якого типу надкласу. Це називається оновленням. Оновлення відбувається автоматично, тоді як програму передачі каналів повинен робити вручну програміст , і я зроблю все можливе, щоб пояснити, чому так.

Оновлення та зрив НЕ схожі на передачу примітивів одне одному, і я вважаю, що це викликає багато плутанини, коли програміст починає вивчати кастинг об'єктів.

Поліморфізм: Усі методи у Java за замовчуванням є віртуальними. Це означає, що будь-який метод може бути відмінений при використанні у спадщину, якщо цей метод не оголошений як остаточний або статичний .

Нижче ви можете побачити приклад того, як getType();працює залежно від типу об'єкта (собака, домашня тварина, поліцейська собака).

Припустимо, у вас є три собаки

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

  2. Pet Dog - Pet Dog розширює собаку.

  3. Поліцейська собака - Поліцейська собака розширює собаку.

    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()методу. Для того, щоб викликати цей метод, вам потрібно перенести цей obj3 вручну на PoliceDog

  ( (PoliceDog)obj3).secretID();

який друкує ID

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

  ( (PetDog)obj2).dogName();

Чому це так, що оновлення є автоматичним, але посилання вниз повинен бути ручним? Ну, бачите, оновлення ніколи не може провалитися. Але якщо у вас є група різних собак і хочуть їх всіх зворотне приведення до їх типу, то є шанс, що деякі з цих собак насправді різних типів , тобто, PetDog, PoliceDogі процес виходить з ладу, шляхом метання ClassCastException.

Це є причиною, коли вам потрібно вручну підключити свої об'єкти, якщо ви віднесли свої об'єкти до типу суперкласу.

Примітка. Тут, посилаючись на те, що ви не змінюєте адресу пам'яті своїх ojects, коли ви спустили її, вона все одно залишається такою ж, ви просто групуєте їх у певний тип. Dog


"Поліморфізм використовує автоматичне відключення під час викликів методів". Ні, це не так. Використовуваний механізм не вказаний, але найбільш звичайний механізм - vtable - такого не робить. Подивіться в об'єктний код. Ніяких опадів.
Маркіз Лорнський

Чому ні? Це те, що відбувається правильно, ви можете навести приклад, коли це не буде працювати?
Nagarjuna Yelisetty

1
Чому ні? Це те, що відбувається правильно. Чи можете ви навести приклад, коли висловлювання "Поліморфізм використовує автоматичне знищення під час викликів методів". не вдасться чи не буде правдою?
Нагарджуна Єлісетті

Це ваша суперечка. Це вам доведеться довести. Покажіть, де в об'єктному коді відбувається заниження. Відповідь на питання "чому ні" - це "тому, що це не потрібно". Vtable дбає про відправлення методу, а змінна вже вказує на весь об'єкт.
Маркіз Лорнський

1
"Наскільки мені відомо, мої твердження є правдивими, і це має місце у кожному випадку" - це не підтвердження. Це просто твердження. Я прошу вас , щоб довести свої твердження. Ти цього не робиш. Насправді ви лише повторюєте себе. І я вже надав кілька спростувань. Я також надав процедуру прийняття рішень. Якщо ви можете знайти занижений код в об'єктному коді для виклику методу, ви маєте рацію, і я помиляюся. Так робиться наука. Зроби це. І стверджувати, що я "сміливо залежу від документації" - це кричуща помилка. Не робіть цього.
Маркіз Лорн

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

Для отримання додаткової інформації, будь ласка, прочитайте цю статтю


хороший момент: ссавець m = (ссавець) новий кіт (); дорівнює ссавцю m = новий Кіт ();
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.

Нам дозволено робити це "природним / неявним / автоматичним" способом, під час компіляції або під час виконання, головним чином тому, що Кіт успадковує частину своєї функціональності від Animal; наприклад, move (). (Принаймні, кішка - тварина, чи не так?)

2.- Зниження.

Але що буде, якщо нам потрібно отримати функціональність Cat від нашого типу Animal tag ?.

Коли ми створили тег 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

Батько: Автомобіль
дитини: Figo
Car c1 = новий Figo ();

=====
Оновлення: -
Метод: Об'єкт c1 буде посилатися на Методи класу (Figo - Метод повинен бути замінений), оскільки для класу "Figo" вказано "new".
Змінна інстанція: Об'єкт c1 буде посилатися на змінну екземпляра класу Декларації ("Автомобіль").

Коли клас декларації є батьківським, а об'єкт створюється дочірнім, то відбувається неявне кастинг, яке є "оновлення".

======
Зниження рівня: -
Figo f1 = (Figo) c1; //
Метод: Об'єкт f1 буде посилатися на Метод класу (figo), оскільки початковий об'єкт c1 створюється з класом "Figo". але після того, як кастинг вниз буде зроблено, методи, які є лише у класі "Figo", також можуть бути передані змінною f1.
Змінна інстанція: Об'єкт f1 не посилатиметься на змінну екземпляра класу Декларації об'єкта c1 (клас оголошення для c1 - CAR), але при зниженні вниз він буде посилатися на змінні екземпляра класу Figo.

======
Використання: Коли об'єкт є дочірнім класом, а клас оголошень є батьківським та дочірнім класом, хоче отримати доступ до змінної інстанції власного класу, а не батьківського класу, то це можна зробити за допомогою "Downcasting".


1

"мовлення" означає закидання об'єкта до супертипу, тоді як посилання "мовлення" означає "піддавання" підтипу.

У Java, оновлення не потрібно, оскільки це робиться автоматично. І зазвичай це називається неявним кастингом. Ви можете вказати це, щоб зрозуміти іншим.

Таким чином, писемність

Animal a = (Animal)d;

або

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");
    }
    ... 
} 

0

Ми можемо створити об’єкт для Downcasting. У цього типу також. : виклик методів базового класу

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.