CodeGym /Blog Java /Random-FR /Élargissement et rétrécissement des types de référence
Auteur
John Selawsky
Senior Java Developer and Tutor at LearningTree

Élargissement et rétrécissement des types de référence

Publié dans le groupe Random-FR
Salut! Dans une leçon précédente, nous avons discuté de la conversion des types primitifs. Rappelons brièvement ce qui a été discuté. Élargissement et rétrécissement des types de référence - 1Nous avons imaginé les types primitifs (dans ce cas, les types numériques) comme des poupées gigognes dont la taille varie en fonction de la quantité de mémoire qu'elles occupent. Comme vous vous en souviendrez, mettre une petite poupée à l'intérieur d'une plus grande est simple à la fois dans la vraie vie et dans la programmation Java.

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
Ceci est un exemple de conversion automatique ou d'élargissement . Cela se produit tout seul, vous n'avez donc pas besoin d'écrire de code supplémentaire. En fin de compte, nous ne faisons rien d'inhabituel : nous mettons simplement une poupée plus petite dans une poupée plus grande. C'est une autre affaire si nous essayons de faire le contraire et de mettre une poupée russe plus grande dans une poupée plus petite. Vous ne pouvez pas faire cela dans la vraie vie, mais en programmation, vous le pouvez. Mais il y a une nuance. Si nous essayons de mettre un intdans une shortvariable, les choses ne se passent pas si bien pour nous. Après tout, la shortvariable ne contient que 16 bits d'information, mais an intoccupe 32 bits ! Par conséquent, la valeur transmise est déformée. Le compilateur nous donnera une erreur (' Mec, tu fais quelque chose de suspect !'). Mais si nous indiquons explicitement le type vers lequel nous convertissons notre valeur, il ira de l'avant et effectuera l'opération.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
C'est exactement ce que nous avons fait dans l'exemple ci-dessus. L'opération a été effectuée, mais comme la shortvariable ne peut accueillir que 16 des 32 octets, la valeur finale est faussée et nous obtenons le nombre -27008 . Une telle opération s'appelle une conversion explicite, ou rétrécissement .

Exemples d'élargissement et de rétrécissement des types de référence

Parlons maintenant des mêmes opérateurs appliqués non pas aux types primitifs, mais aux objets et aux variables de référence ! Comment cela fonctionne-t-il en Java ? C'est en fait assez simple. Il y a des objets qui n'ont aucun lien. Il serait logique de supposer qu'ils ne peuvent pas être convertis l'un à l'autre, ni explicitement ni automatiquement :

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog(); // Error!

   }

}
Ici, bien sûr, nous obtenons une erreur. Les classes Catet Dogne sont pas liées entre elles, et nous n'avons pas écrit de 'convertisseur' pour passer de l'une à l'autre. Il est logique que nous ne puissions pas faire cela : le compilateur n'a aucune idée de comment convertir ces objets d'un type à l'autre. Si les objets sont liés, eh bien, c'est une autre affaire ! Lié comment ? Surtout par héritage. Essayons d'utiliser l'héritage pour créer un petit système de classes. Nous aurons une classe commune pour représenter les animaux :

public class Animal {

   public void introduce() {

       System.out.println("I'm Animal");
   }
}
Tout le monde sait que les animaux peuvent être domestiqués (pets) ou sauvages :

public class WildAnimal extends Animal {

   public void introduce() {

       System.out.println("I'm WildAnimal");
   }
}

public class Pet extends Animal {

   public void introduce() {

       System.out.println("I'm Pet");
   }
}
Prenons par exemple les canidés — nous avons des chiens domestiques et des coyotes :

public class Dog extends Pet {

   public void introduce() {

       System.out.println("I'm Dog");
   }
}



public class Coyote extends WildAnimal {

   public void introduce() {

       System.out.println ("I'm Coyote");
   }
}
Nous avons spécifiquement choisi les classes les plus basiques pour les rendre plus faciles à comprendre. Nous n'avons pas vraiment besoin de champs, et une méthode suffit. Essayons d'exécuter ce code :

public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Selon vous, qu'est-ce qui sera affiché sur la console ? La introduceméthode de la Petclasse ou la Animalclasse sera-t-elle invoquée ? Essayez de justifier votre réponse avant de poursuivre la lecture. Et voici le résultat ! Je suis Pet Pourquoi avons-nous obtenu cela? C'est tout simple. Nous avons une variable parent et un objet descendant. En écrivant,

Animal animal = new Pet();
nous avons élargi une Petréférence et l'avons affectée à une Animalvariable. Comme pour les types primitifs, les types de référence sont automatiquement élargis en Java. Vous n'avez pas besoin d'écrire de code supplémentaire pour que cela se produise. Nous avons maintenant un objet descendant assigné à une référence parent. En conséquence, nous voyons que l'appel de méthode est effectué sur la classe descendante. Si vous ne comprenez toujours pas pourquoi ce code fonctionne, réécrivez-le en langage clair :

Animal animal = new DomesticatedAnimal();
Il n'y a pas de problème avec ça, n'est-ce pas ? Imaginez que c'est la vraie vie, et la référence est simplement une étiquette en papier avec 'Animal' écrit dessus. Si vous prenez ce morceau de papier et que vous l'attachez au collier de n'importe quel animal de compagnie, tout ira bien. Après tout, tout animal de compagnie est un animal ! Le processus inverse - déplacer l'arbre d'héritage vers les descendants - se rétrécit :

public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Comme vous pouvez le voir, nous indiquons clairement ici la classe dans laquelle nous voulons convertir notre objet. Nous avions auparavant une WildAnimalvariable, et maintenant nous avons un Coyote, qui est plus bas sur l'arbre d'héritage. Il est logique que sans une indication explicite, le compilateur n'autorise pas une telle opération, mais si nous indiquons le type entre parenthèses, alors tout fonctionne. Élargissement et rétrécissement des types de référence - 2Prenons un autre exemple plus intéressant :

public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal(); // Error!
   }
}
Le compilateur génère une erreur ! Mais pourquoi? Parce que vous essayez d'affecter un objet parent à une référence descendante. En d'autres termes, vous essayez de faire quelque chose comme ceci :

DomesticatedAnimal domesticatedAnimal = new Animal();
Eh bien, peut-être que tout fonctionnera si nous spécifions explicitement le type vers lequel nous essayons de convertir ? Cela a fonctionné avec les chiffres - Essayons ! :)

public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Exception dans le thread "main" java.lang.ClassCastException : l'animal ne peut pas être converti en animal de compagnie Erreur ! Le compilateur ne nous a pas crié dessus cette fois, mais nous nous sommes retrouvés avec une exception. Nous connaissons déjà la raison : nous essayons d'assigner un objet parent à une référence descendante. Mais pourquoi exactement ne pouvez-vous pas faire cela ? Parce que tous les animaux ne sont pas des animaux domestiques. Vous avez créé un Animalobjet et essayez de l'affecter à une Petvariable. Un coyote est aussi un Animal, mais ce n'est pas un Pet. Autrement dit, lorsque vous écrivez

Pet pet = (Pet) new Animal();
new Animal()pourrait représenter n'importe quel animal, pas nécessairement un animal de compagnie ! Naturellement, votre Pet petvariable ne convient qu'au stockage des animaux de compagnie (et de leurs descendants) et non à n'importe quel type d'animal. C'est pourquoi une exception Java spéciale, ClassCastException, a été créée pour les cas où une erreur se produit lors du transtypage des classes. Revoyons-le à nouveau pour clarifier les choses. Une référence parent peut pointer vers des instances d'une classe descendante :

public class Main {

   public static void main(String[] args) {

       Pet pet = new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Par exemple, ici nous n'avons aucun problème. Nous avons un Petobjet référencé par une Petvariable. Plus tard, une Animalréférence pointait vers le même objet. Après cela, nous convertissons animalen un Pet. Au fait, pourquoi cela a-t-il fonctionné pour nous ? La dernière fois, nous avons eu une exception ! Parce que cette fois notre objet d'origine est un Pet!

Pet pet = new Pet();
Mais dans le dernier exemple, c'était un Animalobjet :

Pet pet = (Pet) new Animal();
Vous ne pouvez pas affecter un objet ancêtre à une variable descendante. Vous pouvez faire le contraire.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION