En java, vous devez explicitement transtyper afin de downcaster une variable
public class Fruit{} // parent class
public class Apple extends Fruit{} // child class
public static void main(String args[]) {
// An implicit upcast
Fruit parent = new Apple();
// An explicit downcast to Apple
Apple child = (Apple)parent;
}
Y a-t-il une raison à cette exigence, à part le fait que java ne fait aucune inférence de type?
Y a-t-il des "accrochages" avec l'implémentation de la conversion automatique vers le bas dans une nouvelle langue?
Par exemple:
Apple child = parent; // no cast required
object-oriented
type-inference
language-design
Sam Washburn
la source
la source
Réponses:
Les retransmissions réussissent toujours.
Les downcasts peuvent entraîner une erreur d'exécution, lorsque le type d'exécution d'objet n'est pas un sous-type du type utilisé dans le transtypage.
Puisque le second est une opération dangereuse, la plupart des langages de programmation typés nécessitent que le programmeur le demande explicitement. Essentiellement, le programmeur dit au compilateur "croyez-moi, je sais mieux - ce sera OK au moment de l'exécution".
En ce qui concerne les systèmes de types, les upcasts imposent la charge de la preuve au compilateur (qui doit le vérifier statiquement), les downcasts imposent la charge de la preuve au programmeur (qui doit y réfléchir sérieusement).
On pourrait faire valoir qu'un langage de programmation correctement conçu interdirait complètement les downcasts, ou fournirait des alternatives de conversion sûres, par exemple en renvoyant un type facultatif
Option<T>
. Cependant, de nombreuses langues répandues ont choisi l’approche la plus simple et la plus pragmatiqueT
et de soulever une erreur autrement.Dans votre exemple spécifique, le compilateur aurait pu être conçu pour déduire qu'il
parent
s'agit en fait d'uneApple
analyse statique simple et permettre la conversion implicite. Cependant, en général, le problème est indécidable, nous ne pouvons donc pas nous attendre à ce que le compilateur effectue trop de magie.la source
Habituellement, le downcasting est ce que vous faites lorsque les connaissances statiquement connues du compilateur sur le type de quelque chose sont moins spécifiques que ce que vous savez (ou du moins espérez).
Dans des situations comme votre exemple, l'objet a été créé en tant
Apple
que puis cette connaissance a été supprimée en stockant la référence dans une variable de typeFruit
. Ensuite, vous souhaitez utiliser à nouveau la même référence qu'unApple
.Étant donné que les informations n'ont été supprimées que "localement", bien sûr, le compilateur peut conserver les connaissances qui
parent
sont vraiment unApple
, même si son type déclaré estFruit
.Mais généralement, personne ne fait ça. Si vous voulez créer un
Apple
et l'utiliser comme unApple
, vous le stockez dans uneApple
variable, pas unFruit
.Lorsque vous en avez un
Fruit
et que vous souhaitez l'utiliser comme un,Apple
cela signifie généralement que vous avez obtenu leFruit
via un moyen qui peut généralement renvoyer tout type deFruit
, mais dans ce cas, vous savez que c'était unApple
. Presque toujours, vous ne l'avez pas simplement construit, vous l'avez passé par un autre code.Un exemple évident est si j'ai une
parseFruit
fonction qui peut transformer des chaînes comme "pomme", "orange", "citron", etc., en sous-classe appropriée; généralement tout ce que nous (et le compilateur) peut savoir sur cette fonction est qu'elle retourne une sorte deFruit
, mais si je l' appelleparseFruit("apple")
alors je sais que ce va appeler unApple
et peut vouloir utiliserApple
des méthodes, donc je pouvais baissés.Encore une fois, un compilateur suffisamment intelligent pourrait comprendre cela en insérant le code source de
parseFruit
, car je l'appelle avec une constante (à moins qu'il ne soit dans un autre module et que nous ayons une compilation séparée, comme en Java). Mais vous devriez facilement pouvoir voir comment des exemples plus complexes impliquant des informations dynamiques pourraient devenir plus difficiles (voire impossibles!) À vérifier par le compilateur.Dans le code, les downcasts se produisent généralement lorsque le compilateur ne peut pas vérifier que le downcast est sûr à l'aide de méthodes génériques, et pas dans des cas aussi simples que de suivre immédiatement un upcast en jetant le même type d'informations que nous essayons de récupérer par downcasting.
la source
Il s'agit de savoir où voulez-vous tracer la ligne. Vous pouvez concevoir un langage qui détectera la validité d'un abaissement implicite:
Maintenant, extrayons une méthode:
Nous sommes toujours bons. L'analyse statique est beaucoup plus difficile mais toujours possible.
Mais le problème surgit dès que quelqu'un ajoute:
Maintenant, où voulez-vous soulever l'erreur? Cela crée un dilemme, on peut dire que le problème est
Apple child = parent;
présent, mais cela peut être réfuté par "Mais ça a marché avant". D'un autre côté, l'ajout aeat(trouble);
causé le problème ", mais tout l'intérêt du polymorphisme est de permettre exactement cela.Dans ce cas, vous pouvez faire une partie du travail du programmeur, mais vous ne pouvez pas tout faire. Plus vous allez loin avant d'abandonner, plus il sera difficile d'expliquer ce qui a mal tourné. Il est donc préférable de s'arrêter le plus tôt possible, selon le principe du signalement précoce des erreurs.
BTW, en Java, le downcast que vous avez décrit n'est pas réellement un downcast. Il s'agit d'une fonte générale, qui peut également couler des pommes en oranges. Donc, techniquement parlant, l'idée de @ chi est déjà là, il n'y a pas de downcasts en Java, seulement des "everycasts". Il serait judicieux de concevoir un opérateur de conversion vers le bas spécialisé qui générera une erreur de compilation lorsque son type de résultat ne peut pas être trouvé en aval de son type d'argument. Ce serait une bonne idée de rendre "Everycast" beaucoup plus difficile à utiliser afin de décourager les programmeurs de l'utiliser sans très bonne raison. La
XXXXX_cast<type>(argument)
syntaxe de C ++ me vient à l'esprit.la source