Duplicata possible:
Comment fonctionne l'opérateur Java cast cast?
implémentation Java Cast
Je me demande toujours comment fonctionne le casting d'objets en Java. Je comprends que pour le type primitif, ce sera plus comme au niveau de la représentation binaire, mais qu'en est-il de l'objet? Est-ce un peu comme Polymorphism
ou dynamic binding
en ce que tout sera déterminé lors de l'exécution? Par exemple:
class Parent{
void A(){}
}
class Child extends Parent{
@Override
void A(){}
}
Parent p = new Parent();
Child c = (Child) p;
Comment ça marche derrière la scène? Crée-t-il une nouvelle instance de Child
? Et aussi, que se passe-t-il si vous essayez de lancer:
Child b = (Child) new Object();
Et le dernier, lors du cast d'une primitive dans une classe wrapper:
Double d = (Double) 3.3;
Je sais que vous n'avez pas besoin de le lancer, mais si vous le faites? Y a-t-il quelque chose d'important qui se passe sur le backend?
Aucun nouvel objet n'est créé dans le système lorsque vous utilisez un transtypage explicite (sauf dans votre dernier cas, où vous transtypez un type primitif en wrapper d'objet , car double
n'est pas un objet comme Double
est). Notez que cette distribution explicite n'est pas nécessaire en raison de Java fonctionnalité de mise en boîte automatique .
Dans votre scénario (Child) new Object()
, Vous recevrez un ClassCastException car un Object
n'est pas un Child
(bien que l'inverse soit vrai).
La réponse à votre premier scénario est la plus compliquée. Essentiellement, la classe parente est traitée comme une interface pourrait l'être. Lorsque vous transtypez Child
en Parent
, seule l'API Parent
est disponible. Cependant, la méthode substituée sera toujours appelée . Donc, si vous le faites:
Parent p = (Parent) new Child();
p.a();
... la public void a()
de Child
sera appelée, même si elle est vue à travers l'objectif de la classe Parent
. Cependant, si vous deviez avoir une deuxième méthode dans le Child
que le Parent
n'a pas (disons public void b()
par exemple), vous le feriez pas pouvoir appeler cela sans redéfinir l'objet dans un Child
.
"Dans les coulisses", comme vous le dites, la seule chose nouvelle qui est créée est une autre référence d'objet qui pointe vers le même objet. Vous pouvez avoir autant de références que vous le souhaitez pour le même objet singulier. Considérez cet exemple:
Parent p = new Parent();
Parent p1 = p;
Parent p2 = p;
Parent p3 = p2;
Ici, il y a quatre références (p
, p1
, p2
Et p3
) Chacune pointant vers le même objet que vous avez créé avec le new Parent()
déclaration.
Je dirais probablement sur le point philosophique, cependant, que cette création de nouvelles références est en fait explicite plutôt que dans les coulisses lorsque vous dites Parent p = something
.
Liens:
La réponse simple à votre question principale est Non. Tous les transtypages ont lieu au moment de la vérification de la syntaxe.
Le cast affecte la façon dont le vérificateur de syntaxe regarde l'objet, il n'affecte pas l'objet lui-même, un cast enfant pour être un parent, est toujours un enfant.
Cependant, la distribution n'est vérifiée qu'au Runtime. C'est pourquoi il est dangereux et ne doit pas être utilisé sauf s'il n'y a pas d'autre moyen.
Selon ceci: checkcast , ce qu'il fait est de vérifier si la référence est assignable. Si c'est le cas, la pile n'est pas modifiée et les opérations sur cette référence sont conservées.
Donc si vous avez:
Child c = ( Child ) anyObject;
c.sayHi();
Si le transtypage réussit, la méthode sayHi
pourrait être invoquée:
Si objectref peut être converti en classe, tableau ou type d'interface résolus, la pile d'opérandes est inchangée; sinon, l'instruction checkcast lève une exception ClassCastException.
Voici le "bytecode"
$ cat CastDemo.Java
class Parent {}
class Child extends Parent {}
class Main {
Child c = (Child) new Parent();
}
$ javap -c Main
Compiled from "CastDemo.Java"
class Main {
Child c;
Main();
Code:
0: aload_0
1: invokespecial #1 // Method Java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class Parent
8: dup
9: invokespecial #3 // Method Parent."<init>":()V
12: checkcast #4 // class Child
15: putfield #5 // Field c:LChild;
18: return
}
Tout d'abord, faites très attention à ne pas confondre la conversion avec le casting . Ils peuvent partager la syntaxe de surface, mais ce sont des processus très différents.
Dans Java vous pouvez abattre un objet vers n'importe quel type, mais au moment de l'exécution, vous obtiendrez un ClassCastException
si l'objet n'est en fait pas compatible avec le type cible. Cela se produit à le niveau de bytecode: il existe une instruction de bytecode dédiée au downcasting.
Child c = (Child) new Object();
entraînera inconditionnellement un ClassCastException
.
Double d = 3.3; // note: no explicit casting needed
exécutera autoboxing dans une instance de Double
. Donc, ici, une nouvelle instance est réellement créée.
Un dowcast normal et réussi peut ressembler à ceci:
Object o = "a";
String s = (String)o;
Ici, aucun objet n'est créé: seule la valeur de o
est copiée dans s
. La valeur est une référence.
La descente d'un objet ne fait rien à cet objet. Dans les coulisses, le compilateur injectera checkcast
opération de bytecode. Si p
n'est pas vraiment une instance de Child
, une exception sera levée. Sinon, vous avez essentiellement une référence (de type) sûre au même objet avec un type différent et plus spécifique.
Child b = (Child) new Object();
Cela échoue avec ClassCastException
. JVM compare getClass()
de new Object()
avec Child.class
. Puisque Object.class
N'est pas une sous-classe de Child.class
, Une exception est levée.
Double d = (Double) 3.3;
Ici, le casting n'est même pas nécessaire, cela fonctionne aussi: Double d = 3.3
. Dans les coulisses, cela se traduit par:
Double d = Double.valueOf(3.3);
Ceci est connu sous le nom de Autoboxing .