Quelle est la différence entre le casting ascendant et descendant par rapport à la variable de classe

138

Quelle est la différence entre la conversion ascendante et descendante par rapport à la variable de classe?

Par exemple, dans la classe de programme suivante, Animal ne contient qu'une seule méthode mais la classe Dog contient deux méthodes, puis comment nous convertissons la variable Dog en variable animale.

Si le casting est fait, comment pouvons-nous appeler le Dog's une autre méthode avec la variable 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();
    }
}
Dhivakar
la source
A Dogest un Animal. La plupart du temps, la conversion ascendante n'est pas nécessaire, sauf si vous souhaitez utiliser une certaine méthode surchargée. callmeexiste à la fois dans Animalet Dog. callme2seulement existe dans Dog, que vous lancez aà Dogfaire fonctionner.
Brian
Quelle est la sortie de votre code?
Malwinder Singh
La chose intéressante est que d.callme retourne 'In callme of Dog' bien que d ait été jeté à animal !!
Chris311
4
@ Chris311 'd' et 'a' pointent tous les deux vers le même objet ... qui est un chien, mais 'a' n'a accès qu'aux méthodes spécifiques à Dog lorsqu'il est abattu au moment de l'exécution. En fait: Animal a = (Animal) d; n'est pas nécessaire, vous n'avez besoin que d'Animal a = d; comme vous êtes upcasting.
Mark Keen

Réponses:

223

La diffusion ascendante est une diffusion vers un supertype, tandis que la diffusion descendante est une diffusion vers un sous-type. L'upcasting est toujours autorisé, mais downcasting implique une vérification de type et peut lancer un ClassCastException.

Dans votre cas, un casting de a Dogà an Animalest un upcast, car a Dogis-a Animal. En général, vous pouvez effectuer une conversion ascendante chaque fois qu'il existe une relation is-a entre deux classes.

Le downcasting serait quelque chose comme ceci:

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

En gros, ce que vous faites, c'est dire au compilateur que vous savez quel est réellement le type d'exécution de l'objet . Le compilateur autorisera la conversion, mais insérera toujours une vérification de l'intégrité d'exécution pour s'assurer que la conversion a du sens. Dans ce cas, le cast est possible car au moment de l'exécution animalest en fait un Dogmême si le type statique de animalest Animal.

Cependant, si vous deviez faire ceci:

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

Vous obtiendrez un ClassCastException. La raison en est que animalle type de runtime de est Animal, et donc quand vous dites au runtime de lancer le cast, il voit que ce animaln'est pas vraiment un Doget lance donc un ClassCastException.

Pour appeler la méthode d'une superclasse, vous pouvez faire super.method()ou en effectuant l'upcast.

Pour appeler la méthode d'une sous-classe, vous devez effectuer un downcast. Comme indiqué ci-dessus, vous risquez normalement un ClassCastExceptionen faisant cela; cependant, vous pouvez utiliser l' instanceofopérateur pour vérifier le type d'exécution de l'objet avant d'effectuer le cast, ce qui vous permet d'empêcher 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;
}
awksp
la source
Un downcasting approprié garantit-il non ClassCastExceptionou non? Comme dans le premier cas?
Malwinder Singh
@MS Qu'entendez-vous par «bon»?
awksp
2
@awksp C'est une réponse excellente et articulée. Résume à peu près tout ce que j'ai besoin de savoir sur le casting.
Gautham Honnavara
Vous n'avez sûrement pas fait de cours où l'animal est une instance de chien, n'est-ce pas? alors pourquoi le vérifiez-vous?
barlop
62

Down-casting et up-casting était comme suit:
entrez la description de l'image ici

Upcasting : Lorsque nous voulons convertir une sous-classe en Super classe, nous utilisons Upcasting (ou élargissement). Cela se produit automatiquement, pas besoin de faire quoi que ce soit explicitement.

Downcasting : Lorsque nous voulons convertir une classe Super en sous-classe, nous utilisons Downcasting (ou rétrécissement), et Downcasting n'est pas directement possible en Java, nous devons explicitement le faire.

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
Premraj
la source
Donc, il n'y a pas un tel moyen de faire une conversion ascendante pour appeler le parent de la méthode?
karlihnos
32

L'upcasting et downcasting sont une partie importante de Java, qui nous permet de construire des programmes compliqués en utilisant une syntaxe simple, et nous donne de grands avantages, comme le polymorphisme ou le regroupement d'objets différents. Java permet à un objet d'un type de sous-classe d'être traité comme un objet de n'importe quel type de superclasse. C'est ce qu'on appelle l'upcasting. L'upcasting est fait automatiquement, tandis que downcasting doit être fait manuellement par le programmeur , et je vais faire de mon mieux pour expliquer pourquoi.

L'upcasting et le downcasting ne sont PAS comme la conversion de primitives de l'une à l'autre, et je crois que c'est ce qui cause beaucoup de confusion, lorsque le programmeur commence à apprendre à lancer des objets.

Polymorphisme: toutes les méthodes de java sont virtuelles par défaut. Cela signifie que toute méthode peut être remplacée lorsqu'elle est utilisée dans l'héritage, à moins que cette méthode ne soit déclarée comme finale ou statique .

Vous pouvez voir l'exemple ci-dessous comment getType();fonctionne selon le type d'objet (chien, animal domestique, chien policier).

Supposons que vous ayez trois chiens

  1. Chien - C'est la super classe.

  2. Pet Dog - Pet Dog étend le chien.

  3. Police Dog - Police Dog étend 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";
     }
    }

Polymorphisme: toutes les méthodes de java sont virtuelles par défaut. Cela signifie que toute méthode peut être remplacée lorsqu'elle est utilisée dans l'héritage, sauf si cette méthode est déclarée comme finale ou statique. (Explication appartient au concept de tables virtuelles)

Table virtuelle / table de répartition: La table de répartition d'un objet contiendra les adresses des méthodes liées dynamiquement de l'objet. Les appels de méthode sont effectués en récupérant l'adresse de la méthode dans la table de répartition de l'objet. La table de répartition est la même pour tous les objets appartenant à la même classe et est donc généralement partagée entre eux.

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

Tirages Normal Dog

  obj2.getType();

Tirages Pet Dog

 obj3.getType();

Tirages Police Dog

Le downcasting doit être effectué manuellement par le programmeur

Lorsque vous essayez d'appeler la secretID();méthode sur obj3laquelle est PoliceDog objectréférencée mais à Doglaquelle est une super-classe dans la hiérarchie, cela génère une erreur car vous obj3n'avez pas accès à la secretId()méthode. Afin d'appeler cette méthode, vous devez downcast manuellement cet obj3 pour PoliceDog

  ( (PoliceDog)obj3).secretID();

qui imprime ID

Dans la même manière d'invoquer la dogName();méthode PetDogclasse , vous devez downcaster obj2à PetDogdepuis obj2 est référencé Doget ne pas avoir accès à la dogName();méthode

  ( (PetDog)obj2).dogName();

Pourquoi est-ce ainsi, que la conversion ascendante est automatique, mais que la prévision descendante doit être manuelle? Eh bien, vous voyez, l'upcasting ne peut jamais échouer. Mais si vous avez un groupe de chiens différents et que vous voulez les downcaster tous à leurs types, alors il y a une chance, que certains de ces chiens sont en fait de différents types iE, PetDog, PoliceDoget le processus échoue, en jetant ClassCastException.

C'est la raison pour laquelle vous devez réduire manuellement vos objets si vous avez référencé vos objets au type de super classe.

Remarque: Ici, le référencement signifie que vous ne changez pas l'adresse mémoire de vos objets lorsque vous le downcastez, il reste toujours le même, vous les regroupez simplement dans un type particulier dans ce cas Dog

Nagarjuna Yelisetty
la source
«Le polymorphisme utilise un downcast automatique lors des appels de méthode.» Non, ce n'est pas le cas. Le mécanisme utilisé n'est pas spécifié, mais le mécanisme le plus courant - une table - ne fait rien de tel. Regardez dans le code objet. Pas abattu.
Marquis of Lorne
Pourquoi pas? C'est ce qui se passe correctement. Pouvez-vous donner un exemple où cela ne fonctionnera pas?
Nagarjuna Yelisetty
1
Pourquoi pas? C'est ce qui se passe correctement ... pouvez-vous donner un exemple où l'instruction «Le polymorphisme utilise un downcast automatique pendant les appels de méthode». échouera ou ne sera pas vrai?
Nagarjuna Yelisetty
C'est votre argument. C'est à vous de le prouver. Montre où dans le code objet le downcast se produit. La réponse à la question «pourquoi pas» est «parce que ce n'est pas nécessaire». La vtable s'occupe de la répartition de la méthode et la variable pointe déjà vers l'objet entier.
Marquis of Lorne
1
«À ma connaissance, mes déclarations sont vraies et cela vaut dans tous les cas» n'est pas une preuve. C'est une simple affirmation. Je demande que vous de prouver vos déclarations. Vous ne le faites pas. En fait, vous ne faites que vous répéter. Et j'ai déjà fourni plusieurs réfutations. J'ai également fourni une procédure de décision. Si vous pouvez trouver un downcast dans le code objet pour un appel de méthode, vous avez raison et je me trompe. C'est ainsi que se fait la science. Fais le. Et prétendre que je suis «hardiment en fonction de la documentation» est une fausse déclaration flagrante. Ne fais pas ça.
Marquis of Lorne
12

Je connais cette question posée il y a assez longtemps mais pour les nouveaux utilisateurs de cette question. Veuillez lire cet article qui contient une description complète de la montée en puissance, de la descente et de l'utilisation de l' opérateur instanceof

  • Il n'y a pas besoin de remonter manuellement, cela se produit tout seul:

    Mammal m = (Mammal)new Cat(); égale à Mammal m = new Cat();

  • Mais le downcasting doit toujours être fait manuellement:

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

Pourquoi est-ce ainsi, que la conversion ascendante est automatique, mais que la prévision descendante doit être manuelle? Eh bien, vous voyez, l'upcasting ne peut jamais échouer. Mais si vous avez un groupe d'animaux différents et que vous voulez les réduire tous à un chat, alors il y a une chance que certains de ces animaux soient en fait des chiens, et le processus échoue, en lançant ClassCastException. C'est là que devrait introduire une fonctionnalité utile appelée "instanceof" , qui teste si un objet est une instance d'une classe.

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

Pour plus d'informations, veuillez lire cet article

Nadeesha Thilakarathne
la source
bon point: Mammifère m = (Mammifère) nouveau Chat (); égal à Mammifère m = nouveau chat ();
Catbuilts
6

Mieux vaut essayer cette méthode pour upcasting, c'est facile à comprendre:

/* 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();
    }
}
UWAIS
la source
et dans la dernière ligne cela pourrait être: ((Dog) ref) .callme2 (); // pour downcasting / rétrécissement et accès à la méthode callme2 () de la classe Dog.
udarH3
6

Peut-être que ce tableau aide. Appel de la callme()méthode de la classe Parentou de la classe Child. En principe:

UPCASTING -> Cacher

DOWNCASTING -> Révéler

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

Alexis
la source
4

1.- Upcasting.

En faisant un upcasting, vous définissez une balise d'un certain type, qui pointe vers un objet d'un sous-type (le type et le sous-type peuvent être appelés classe et sous-classe, si vous vous sentez plus à l'aise ...).

Animal animalCat = new Cat();

Ce qui signifie qu'une telle balise, animalCat, aura la fonctionnalité (les méthodes) de type Animal uniquement, car nous l'avons déclarée en tant que type Animal, pas en tant que type Cat.

Nous sommes autorisés à le faire de manière "naturelle / implicite / automatique", à la compilation ou à l'exécution, principalement parce que Cat hérite d'une partie de ses fonctionnalités d'Animal; par exemple, move (). (Au moins, le chat est un animal, n'est-ce pas?)

2.- Downcasting.

Mais que se passerait-il si nous devions obtenir la fonctionnalité de Cat, à partir de notre étiquette de type Animal ?.

Comme nous avons créé la balise animalCat pointant vers un objet Cat, nous avons besoin d'un moyen d'appeler les méthodes d'objet Cat, à partir de notre balise animalCat, d'une manière assez intelligente.

Une telle procédure est ce que nous appelons le Downcasting, et nous ne pouvons le faire qu'au moment de l'exécution.

Temps pour un peu de code:

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

}
Lemmy_Caution
la source
2

Parent: Voiture
Enfant: Figo
Voiture c1 = nouveau Figo ();

=====
Upcasting: -
Méthode: L'objet c1 fera référence aux méthodes de classe (Figo - La méthode doit être remplacée) car la classe "Figo" est spécifiée avec "new".
Variable d'instance: l'objet c1 se référera à la variable d'instance de la classe de déclaration ("Car").

Lorsque la classe de déclaration est le parent et que l'objet est créé pour l'enfant, un cast implicite se produit, ce qui est "Upcasting".

======
Downcasting: -
Figo f1 = (Figo) c1; //
Méthode: L'objet f1 fera référence à la méthode de la classe (figo) car l'objet initial c1 est créé avec la classe "Figo". mais une fois la conversion descendante effectuée, les méthodes qui ne sont présentes que dans la classe "Figo" peuvent également être référencées par la variable f1.
Variable d'instance: L'objet f1 ne fera pas référence à la variable d'instance de la classe de déclaration de l'objet c1 (la classe de déclaration pour c1 est CAR) mais avec la conversion descendante, il se référera aux variables d'instance de la classe Figo.

======
Utilisation: Lorsque l'objet est de la classe enfant et que la classe de déclaration est parent et que la classe enfant veut accéder à la variable Instance de sa propre classe et non de la classe parent, cela peut être fait avec "Downcasting".

amit shah
la source
1

upcasting signifie convertir l'objet en un supertype, tandis que downcasting signifie lancer un sous-type.

En java, la diffusion ascendante n'est pas nécessaire car elle est effectuée automatiquement. Et c'est généralement appelé casting implicite. Vous pouvez le spécifier pour le rendre clair aux autres.

Ainsi, l'écriture

Animal a = (Animal)d;

ou

Animal a = d;

conduit exactement au même point et dans les deux cas sera exécuté le callme()from Dog.

Le downcasting est plutôt nécessaire car vous avez défini acomme objet d'Animal. Actuellement, vous savez que c'est un Dog, mais java n'a aucune garantie que ce soit. En fait, au moment de l'exécution, cela pourrait être différent et java lancera un ClassCastException, si cela se produisait. Bien sûr, ce n'est pas le cas de votre exemple. Si vous ne jeter aà Animal, java ne pouvait même pas compiler l'application parce que Animalne possède pas la méthode callme2().

Dans votre exemple , vous ne pouvez pas atteindre le code de callme()de Animalde UseAnimlas(parce que l' Dogécraser) à moins que la méthode serait la suivante:

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

Nous pouvons créer un objet à Downcasting. Dans ce type également. : appel des méthodes de classe de base

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
Rakesh
la source