Quelqu'un peut-il m'expliquer comment le compilateur ne se plaint pas dans le premier casting, mais se plaint dans le second?
interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }
public class Test{
public static void main(){
C1 o1 = new C1();
C2 o2 = new C2();
Integer o3 = new Integer(4);
I2 x = (I2)o1; //compiler does not complain
I2 y = (I2)o3; //compiler complains here !!
}
}
Lorsque vous lancez o1
et o3
avec (I2)
, vous dites au compilateur que la classe de l'objet est en fait une sous-classe de son type déclaré, et que cette sous-classe implémente I2
.
La classe Integer
est final, donc o3
ne peut pas être une instance d'une sous-classe de Integer
: le compilateur sait que vous mentez. C1
n'est cependant pas définitif, donc o1
pourrait être une instance d'un sous-type de C1
qui implémente I2
.
Si tu fais C1
final, le compilateur se plaindra aussi:
interface I1 { }
interface I2 { }
final class C1 implements I1 { }
class C2 implements I2 { }
public class Test{
public static void main(){
C1 o1 = new C1();
C2 o2 = new C2();
Integer o3 = new Integer(4);
I2 y = (I2)o3; //compiler complains here !!
I2 x = (I2)o1; //compiler complains too
}
}
Selon JLS chapitre 5
Étant donné un type de référence au moment de la compilation S (source) et un type de référence au moment de la compilation T (cible), une conversion de transtypage existe de S vers T si aucune erreur au moment de la compilation ne se produit en raison des règles suivantes. Si T est un type d'interface:
Si S n'est pas une classe finale (§8.1.1), alors, s'il existe un supertype X de T, et un supertype Y de S, tels que X et Y sont tous les deux des types paramétrés distinctement prouvés, et que les effacements de X et Y sont identiques, une erreur de compilation se produit.
Sinon, la distribution est toujours légale au moment de la compilation (car même si S n'implémente pas T, une sous-classe de S pourrait).
Si S est une classe finale (§8.1.1), alors S doit implémenter T, sinon une erreur de compilation se produit.
En effet, la classe Integer
est finale et C1
n'est pas. Ainsi, un objet Integer ne peut pas implémenter I2, alors qu'un objet C1 le pourrait s'il s'agit d'une instance d'une sous-classe de C1 qui implémente I2.
Selon JLS 5.5.1 - Casting du type de référence , la ou les règles s'appliquent:
Si T est un type de classe, alors | S | <: | T |, ou | T | <: | S |. Sinon, une erreur de compilation se produit.
I2 y = (I2)o3; //compiler complains here !!
Dans ce cas, un Integer
et I2
sont sans aucun lien , donc une erreur de compilation se produit. De plus, comme Integer
est final
, il n'y a pas de relation entre Integer
et I2
.
I2
et I1
peut être lié car les deux sont une interface marqueur (il n'y a pas de contrat).
Quant au code compilé, la règle suit:
S
est o1
et T
est I2
.
J'espère que cela t'aides.