53 votes

Pourquoi javac permet-il certains castings impossibles et pas d'autres ?

Si j'essaie de lancer un String à un java.util.Date le compilateur Java détecte l'erreur. Alors pourquoi le compilateur ne signale-t-il pas l'erreur suivante ?

List<String> strList = new ArrayList<>();                                                                      
Date d = (Date) strList;

Bien sûr, la JVM lance un ClassCastException au moment de l'exécution, mais le compilateur ne le signale pas.

Le comportement est le même avec javac 1.8.0_212 et 11.0.2.

2 votes

Rien de spécial à propos de List ici. Date d = (Date) new Object();

1 votes

J'ai joué avec un arduino dernièrement. J'adorerais un compilateur qui n'accepterait pas joyeusement n'importe quel cast et qui le ferait avec des résultats totalement imprévisibles. String to integer ? C'est sûr ! Double en entier ? Oui, monsieur ! Chaîne en booléen ? Au moins celui-là devient le plus souvent faux...

0 votes

@ElliottFrisch : Il y a une relation d'héritage évidente entre Date et Object, mais il n'y a pas de relation entre Date et List. Je m'attendais donc à ce que le compilateur signale ce cast, de la même manière qu'il signalerait un cast de String à Date. Mais comme l'explique Zabuza dans son excellente réponse, List est une interface, donc le cast serait légal si strList était une instance d'une classe qui implémente List.

88voto

Zabuza Points 11045

Les acteurs est techniquement possible. Il n'est pas facile de prouver par javac qu'il n'en est pas ainsi dans votre cas et le JLS définit effectivement ceci comme un programme Java valide, donc signaler une erreur serait incorrect.

Cela s'explique par le fait que List est une interface. Ainsi, vous pourriez avoir une sous-classe d'une Date qui implémente réellement List déguisé en List ici - et ensuite l'intégrer à Date serait parfaitement acceptable. Par exemple :

public class SneakyListDate extends Date implements List<Foo> {
    ...
}

Et puis :

List<Foo> list = new SneakyListDate();
Date date = (Date) list; // This one is valid, compiles and runs just fine

La détection d'un tel scénario n'est pas toujours possible, car elle nécessiterait des informations au moment de l'exécution si l'instance provient, par exemple, d'une méthode à la place. Et même si c'était le cas, cela demanderait beaucoup plus d'efforts au compilateur. Le compilateur n'empêche que les casts qui sont absolument impossibles parce qu'il n'y a aucun moyen pour le class-tree de correspondre du tout. Ce qui n'est pas le cas ici, comme on le voit.

Notez que le JLS exige que votre code soit un programme Java valide. Dans 5.1.6.1. Conversion de la référence de rétrécissement autorisée il est dit :

Une conversion de référence étroite existe à partir du type de référence S au type de référence T si todo des éléments suivants sont vrai :

  • [...]
  • Un des cas suivants s'applique :
    • [...]
    • S est un type d'interface, T est un type de classe, et T ne nomme pas un final classe.

Donc, même si le compilateur podría se rend compte que votre cas est en fait provablement impossible, il n'est pas autorisé à signaler une erreur car le JLS le définit comme un programme Java valide.

Il ne serait autorisé qu'à afficher un avertissement.

16 votes

Et il faut noter que la raison pour laquelle le cas de String est pris en compte est que String est final, donc le compilateur sait qu'aucune classe ne peut l'étendre.

5 votes

En fait, je ne pense pas que ce soit la "finalité" de String qui rende myDate = (Date) myString échouer. En utilisant la terminologie JLS, l'instruction tente de convertir de S (le String ) à T (le Date ). Ici, S n'est pas un type d'interface, donc la condition JLS citée ci-dessus ne s'applique pas. Par exemple, si vous essayez de convertir un calendrier en une date, vous obtiendrez une erreur de compilation, même si aucune des deux classes n'est finale.

0 votes

Il faisait probablement référence au fait de lancer un List à String par opposition à la coulée List à Date . Former ne compile pas car String n'applique pas List et il est impossible de l'étendre davantage pour introduire List parce que la classe est final .

16voto

Stephen C Points 255558

Considérons une généralisation de votre exemple :

List<String> strList = someMethod();       
Date d = (Date) strList;

Ce sont les principales raisons pour lesquelles Date d = (Date) strList; n'est pas une erreur de compilation.

  • El raison intuitive est que le compilateur ne connaît pas (en général) le type précis de l'objet retourné par cet appel de méthode. Il est possible qu'en plus d'être une classe qui implémente la méthode List c'est también une sous-classe de Date .

  • El raison technique c'est que la spécification du langage Java "autorise" les conversion de référence étroite qui correspond à cette distribution de type. Selon JLS 5.1.6.1 :

    "Une conversion de référence rétrécie existe à partir du type de référence S au type de référence T si toutes les conditions suivantes sont remplies :"

    ...

    5) " S est un type d'interface, T est un type de classe, et T ne nomme pas un final classe".

    ...

    À un autre endroit, JLS indique également qu'une exception peut être levée au moment de l'exécution ...

    Notez que la détermination de JLS 5.1.6.1 est basée sur uniquement sur les types déclarés des variables impliquées plutôt que sur les types réels d'exécution. Dans le cas général, le compilateur ne connaît pas et ne peut pas connaître les types réels du temps d'exécution.


Alors, pourquoi le compilateur Java n'arrive-t-il pas à comprendre que le cast ne fonctionne pas ?

  • Dans mon exemple, le someMethod peut renvoyer des objets de différents types. Même si le compilateur était capable d'analyser le corps de la méthode et de déterminer l'ensemble précis des types pouvant être renvoyés, rien n'empêche quelqu'un de la modifier pour qu'elle renvoie des types différents ... après avoir compilé le code qui l'appelle. C'est la raison fondamentale pour laquelle JLS 5.1.6.1 dit ce qu'il dit.

  • Dans votre exemple, un compilateur intelligent podría comprendre que le casting ne peut jamais réussir. Et il est permis d'émettre un temps de compilation avertissement pour signaler le problème.

Alors pourquoi un compilateur intelligent n'est-il pas autorisé à dire que c'est une erreur de toute façon ?

  • Parce que le JLS dit que c'est un programme valide. Période. Tout compilateur qui appelle cela un erreur ne serait pas conforme à Java.

  • De même, tout compilateur qui rejette les programmes Java que les programmes JLS et autre que les compilateurs disent être valides, est un obstacle à la portabilité du code source Java.

4 votes

Upvote pour le fait que après la compilation de la classe appelante, l'implémentation de la fonction appelée peut changer Ainsi, même s'il est possible de prouver au moment de la compilation, avec l'implémentation actuelle du demandeur, que le transfert est impossible, ce n'est pas forcément le cas. à des heures d'exécution ultérieures lorsque l'appelé a changé ou a été remplacé.

2 votes

Upvote pour avoir mis en évidence le problème de portabilité qui serait introduit si un compilateur essaie d'être trop intelligent.

2voto

Oleksandr Points 7545

5.5.1. Moulage de type de référence :

Étant donné un type de référence en temps de compilation S (source) et un temps de compilation type de référence T (cible), il existe une conversion de coulée de S à T si aucune erreur de compilation ne se produit en raison des règles suivantes.

[...]

Si S est un type d'interface :

  • [...]

  • Si T est un type de classe ou d'interface qui n'est pas final, alors s'il existe un supertype X de T et un supertype Y de S de telle sorte que les deux X y Y sont des paramètres prouvés distincts et que les effacements de X y Y sont les mêmes, un erreur de compilation se produit.

    Sinon, le cast est toujours légal au moment de la compilation (parce que même si T ne met pas en œuvre S une sous-classe de T pourrait).

List<String> es S y Date es T dans votre cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X