La conversion ascendante est autorisée en Java, mais la descente en diffusion génère une erreur de compilation.
L'erreur de compilation peut être supprimée en ajoutant un cast, mais serait de toute façon cassée au moment de l'exécution.
Dans ce cas, pourquoi Java autorise le downcasting s'il ne peut pas être exécuté au moment de l'exécution?
Existe-t-il une utilité pratique pour ce concept?
public class demo {
public static void main(String a[]) {
B b = (B) new A(); // compiles with the cast,
// but runtime exception - Java.lang.ClassCastException
}
}
class A {
public void draw() {
System.out.println("1");
}
public void draw1() {
System.out.println("2");
}
}
class B extends A {
public void draw() {
System.out.println("3");
}
public void draw2() {
System.out.println("4");
}
}
Le downcasting est autorisé lorsqu'il est possible qu'il réussisse au moment de l'exécution:
Object o = getSomeObject(),
String s = (String) o; // this is allowed because o could reference a String
Dans certains cas, cela ne réussira pas:
Object o = new Object();
String s = (String) o; // this will fail at runtime, because o doesn't reference a String
Lorsqu'un transtypage (tel que ce dernier) échoue à l'exécution, un ClassCastException
sera lancé.
Dans d'autres cas, cela fonctionnera:
Object o = "a String";
String s = (String) o; // this will work, since o references a String
Notez que certains lancers seront interdits à la compilation, car ils ne réussiront jamais du tout:
Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
En utilisant votre exemple, vous pourriez faire:
public void doit(A a) {
if(a instanceof B) {
// needs to cast to B to access draw2 which isn't present in A
// note that this is probably not a good OO-design, but that would
// be out-of-scope for this discussion :)
((B)a).draw2();
}
a.draw();
}
Je crois que cela s'applique à tous les langages statiques:
String s = "some string";
Object o = s; // ok
String x = o; // gives compile-time error, o is not neccessarily a string
String x = (String)o; // ok compile-time, but might give a runtime exception if o is not infact a String
La conversion de type dit effectivement: supposons qu'il s'agit d'une référence à la classe de distribution et l'utilise comme telle. Maintenant, disons que o est vraiment un entier, en supposant qu'il s'agisse d'une chaîne n'a aucun sens et donnera des résultats inattendus; il doit donc y avoir une vérification à l'exécution et une exception pour informer l'environnement d'exécution que quelque chose est en train de se passer. faux.
Dans la pratique, vous pouvez écrire du code travaillant sur une classe plus générale, mais transposez-le en sous-classe si vous savez de quelle classe il s'agit et que vous devez le traiter comme tel. Un exemple typique est de remplacer Object.equals (). Supposons que nous avons une classe pour voiture:
@Override
boolean equals(Object o) {
if(!(o instanceof Car)) return false;
Car other = (Car)o;
// compare this to other and return
}
Nous pouvons tous voir que le code que vous avez fourni ne fonctionnera pas au moment de l'exécution. C'est parce que nous savons que l'expression new A()
peut jamais être un objet de type B
.
Mais ce n'est pas ainsi que le compilateur le voit. Au moment où le compilateur vérifie si la distribution est autorisée, il ne voit que ceci:
variable_of_type_B = (B)expression_of_type_A;
Et comme d'autres l'ont démontré, ce type de casting est parfaitement légal. L'expression à droite pourrait très bien correspondre à un objet de type B
. Le compilateur voit que A
et B
ont une relation de sous-type. Ainsi, avec la vue "expression" du code, la conversion peut fonctionner.
Le compilateur ne considère pas le cas particulier où il sait exactement quel type d'objet expression_of_type_A
aura réellement. Il voit simplement le type statique comme A
et considère que le type dynamique pourrait être A
ou tout descendant de A
, y compris B
.
Dans ce cas, pourquoi Java autorise le downcasting s'il ne peut pas être exécuté au moment de l'exécution?
Je pense que c’est parce qu’il n’ya aucun moyen pour le compilateur de savoir au moment de la compilation si le casting réussira ou non. Pour votre exemple, il est simple de voir que la distribution échouera, mais il y a d'autres moments où ce n'est pas aussi clair.
Par exemple, imaginez que les types B, C et D étendent tous le type A, puis une méthode public A getSomeA()
renvoie une instance de B, C ou D en fonction d'un nombre généré aléatoirement. Le compilateur ne peut pas savoir quel type d'exécution exact sera renvoyé par cette méthode. Par conséquent, si vous convertissez les résultats ultérieurement en B
, il est impossible de savoir si la conversion réussira (ou échouera). Par conséquent, le compilateur doit supposer que les conversions réussiront.
@ Affiche originale - voir les commentaires en ligne.
public class demo
{
public static void main(String a[])
{
B b = (B) new A(); // compiles with the cast, but runtime exception - Java.lang.ClassCastException
//- A subclass variable cannot hold a reference to a superclass variable. so, the above statement will not work.
//For downcast, what you need is a superclass ref containing a subclass object.
A superClassRef = new B();//just for the sake of illustration
B subClassRef = (B)superClassRef; // Valid downcast.
}
}
class A
{
public void draw()
{
System.out.println("1");
}
public void draw1()
{
System.out.println("2");
}
}
class B extends A
{
public void draw()
{
System.out.println("3");
}
public void draw2()
{
System.out.println("4");
}
}
Downcast fonctionne dans le cas où nous avons affaire à un objet upcasté. Upcasting:
int intValue = 10;
Object objValue = (Object) intvalue;
Alors maintenant, cette variable objValue
peut toujours être convertie en int
car l'objet qui a été lancé est un Integer
,
int oldIntValue = (Integer) objValue;
// can be done
mais comme objValue
est un objet, il ne peut pas être converti en String
car int
ne peut pas être converti en String
.
La transformation descendante des objets n'est pas possible. Seulement "DownCasting1 _downCasting1 = (DownCasting1) ((DownCasting2) downCasting1);" est posible
class DownCasting0 {
public int qwe() {
System.out.println("DownCasting0");
return -0;
}
}
class DownCasting1 extends DownCasting0 {
public int qwe1() {
System.out.println("DownCasting1");
return -1;
}
}
class DownCasting2 extends DownCasting1 {
public int qwe2() {
System.out.println("DownCasting2");
return -2;
}
}
public class DownCasting {
public static void main(String[] args) {
try {
DownCasting0 downCasting0 = new DownCasting0();
DownCasting1 downCasting1 = new DownCasting1();
DownCasting2 downCasting2 = new DownCasting2();
DownCasting0 a1 = (DownCasting0) downCasting2;
a1.qwe(); //good
System.out.println(downCasting0 instanceof DownCasting2); //false
System.out.println(downCasting1 instanceof DownCasting2); //false
System.out.println(downCasting0 instanceof DownCasting1); //false
DownCasting2 _downCasting1= (DownCasting2)downCasting1; //good
DownCasting1 __downCasting1 = (DownCasting1)_downCasting1; //good
DownCasting2 a3 = (DownCasting2) downCasting0; // Java.lang.ClassCastException
if(downCasting0 instanceof DownCasting2){ //false
DownCasting2 a2 = (DownCasting2) downCasting0;
a2.qwe(); //error
}
byte b1 = 127;
short b2 =32_767;
int b3 = 2_147_483_647;
// long _b4 = 9_223_372_036_854_775_807; //int large number max 2_147_483_647
long b4 = 9_223_372_036_854_775_807L;
// float _b5 = 3.4e+038; //double default
float b5 = 3.4e+038F; //Sufficient for storing 6 to 7 decimal digits
double b6 = 1.7e+038;
double b7 = 1.7e+038D; //Sufficient for storing 15 decimal digits
long c1 = b3;
int c2 = (int)b4;
//int 4 bytes Stores whole numbers from -2_147_483_648 to 2_147_483_647
//float 4 bytes Stores fractional numbers from 3.4e−038 to 3.4e+038. Sufficient for storing 6 to 7 decimal digits
float c3 = b3; //logic error
double c4 = b4; //logic error
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Le downcasting est très utile dans l'extrait de code suivant que j'utilise tout le temps. Prouvant ainsi que le downcasting est utile.
private static String printAll(LinkedList c)
{
Object arr[]=c.toArray();
String list_string="";
for(int i=0;i<c.size();i++)
{
String mn=(String)arr[i];
list_string+=(mn);
}
return list_string;
}
Je stocke String dans la liste liée. Lorsque je récupère les éléments de la liste liée, les objets sont renvoyés. Pour accéder aux éléments sous forme de chaînes (ou de tout autre objet de classe), le downcasting m'aide.
Java nous permet de compiler du code downcast en nous faisant croire que nous agissons mal. Pourtant, si les humains commettent une erreur, elle est interceptée au moment de l'exécution.
Considérons l'exemple ci-dessous
public class ClastingDemo {
/**
* @param args
*/
public static void main(String[] args) {
AOne obj = new Bone();
((Bone) obj).method2();
}
}
class AOne {
public void method1() {
System.out.println("this is superclass");
}
}
class Bone extends AOne {
public void method2() {
System.out.println("this is subclass");
}
}
ici, nous créons l’objet de la sous-classe Bone et l’attribuons à la référence AOne de super classe et la référence à la superclasse ne connaît pas la méthode method2 dans la sous-classe, c’est-à-dire Bone au moment de la compilation. la référence résultante peut connaître la présence de méthodes dans la sous-classe, à savoir Bone