Comment la variance d'usage du site de Java comparer à la variance du site de la déclaration de C #?

StackOverflow https://stackoverflow.com/questions/4231305

  •  26-09-2019
  •  | 
  •  

Question

Ma comprendre est que la spécification variance des génériques en C # se produit au niveau de la déclaration de type: lorsque vous créez votre type générique, vous spécifiez la variance pour les arguments de type. En Java, d'autre part, la variance est spécifiée lorsqu'un générique est utilisé. Lorsque vous créez une variable d'un certain type générique, vous indiquez comment ses arguments de type peuvent varier

Quels sont les avantages et les inconvénients de chaque option?

Était-ce utile?

La solution

Je vais juste répondre aux différences entre la déclaration du site et de la variance utilisation du site, puisque, alors que les génériques C # et Java diffèrent dans bien d'autres façons, ces différences sont principalement orthogonale à la variance.

Tout d'abord, si je me souviens écart utiliser le site correctement est strictement plus puissant que la variance déclaration du site (bien au prix de concision), ou tout au moins les caractères génériques de Java sont (qui sont en fait plus puissante que la variance utilisation du site) . Cette puissance accrue est particulièrement utile pour les langues dans lesquelles les constructions stateful sont fortement utilisés, tels que C # et Java (mais Scala beaucoup moins, d'autant plus que ses listes standards sont immuables). Considérez List<E> (ou IList<E>). Comme il a des méthodes pour ajouter E est à la fois et d'obtenir E de, il est invariant par rapport à E, et ainsi la variance déclaration de site ne peut pas être utilisé. Cependant, avec une variance utilisation du site, vous pouvez simplement dire List<+Number> pour obtenir le sous-ensemble covariante de List et List<-Number> pour obtenir le sous-ensemble contravariante de List. Dans une langue déclaration site le concepteur de la bibliothèque devrait faire des interfaces distinctes (ou classes si vous permettez l'héritage multiple de classes) pour chaque sous-ensemble et ont List étendre ces interfaces. Si le concepteur bibliothèque ne le fait pas (notez que IEnumerable de C # ne fait que d'un petit sous-ensemble de la partie covariante de IList), alors vous êtes hors de la chance et vous devez recourir aux mêmes tracas que vous avez à faire dans une langue sans toute sorte de variance.

Alors que ce sont les avantages de l'héritage utilisation du site sur l'héritage déclaration site. L'avantage de l'héritage déclaration du site sur l'héritage utilisation du site est essentiellement pour l'utilisateur concision (à condition que le concepteur est passé par l'effort de séparer chaque classe / interface dans sa covariant et des parties contravariants). Pour quelque chose comme IEnumerable ou Iterator, il est agréable de ne pas avoir à préciser covariance chaque fois que vous utilisez l'interface. Java a fait ce particulièrement gênant en utilisant une syntaxe long (sauf pour les bivariance pour lesquels la solution de Java est essentiellement idéale).

Bien sûr, ces deux caractéristiques linguistiques peuvent coexister. Pour les paramètres de type qui sont naturellement covariant ou contravariant (comme dans IEnumerable / Iterator), le déclarer dans la déclaration. Pour les paramètres de type qui sont naturellement invariant (comme dans (I)List), DÉCLARE quel genre de variance que vous voulez que chaque fois que vous l'utilisez. Il suffit de ne pas spécifier une variance utilisation du site pour les arguments avec une variance déclaration du site comme cela fait juste les choses confuses.

Il y a d'autres questions plus détaillées, je ne suis pas allé dans (comment les jokers sont en réalité plus puissante que la variance utilisation du site), mais j'espère que cela répond à votre question à votre contenu. Je dois admettre que je suis partial envers la variance utilisation du site, mais j'ai essayé de présenter les principaux avantages des deux qui sont venus dans mes discussions avec les programmeurs et avec des chercheurs de langue.

Autres conseils

La plupart des gens semblent préférer la variance déclaration du site, car il est plus facile pour les utilisateurs de la bibliothèque (tout en le rendant un peu plus difficile pour le développeur bibliothèque, même si je dirais que le développeur bibliothèque doit penser à la variance peu importe où la variance est effectivement écrit.)

Mais gardez à l'esprit, que ni Java, ni C # sont des exemples de bonne conception du langage.

Alors que Java à droite de la variance obtenu et travailler indépendamment de la machine virtuelle Java en raison des améliorations VM compatibles Java 5 et le type d'effacement, la variance utilisation du site rend l'utilisation d'une lourdeur de bits et la mise en œuvre particulier de type -erasure a suscité des critiques bien méritées.

C # modèle de "la variance déclaration du site prend la charge loin de l'utilisateur de la bibliothèque, mais lors de leur introduction de médicaments génériques réifiées ils ont construit essentiellement les règles de la variance dans la leur machine virtuelle. Même aujourd'hui, ils ne peuvent pas soutenir pleinement co / contravariance à cause de cette erreur (et la non mise en place rétrocompatible des classes de collection réifiées a divisé les programmeurs en deux camps).

Cela pose une restriction difficile sur toutes les langues ciblant le CLR et est l'une des raisons pour lesquelles les langages de programmation alternatives sont beaucoup plus vive sur la machine virtuelle Java bien qu'il semble que le CLR a « beaucoup de caractéristiques plus agréables ».

Regardons à Scala : Scala est un entièrement orienté objet, hybride fonctionnel en cours d'exécution sur la machine virtuelle Java. Ils utilisent l'effacement de type comme Java, mais aussi la mise en œuvre de Generics et la (déclaration site) variance sont plus faciles à comprendre, plus simple et plus puissant que Java (ou de C # 's), parce que la VM n'impose pas de règles sur la façon dont la variance doit travail. Les contrôles du compilateur Scala les notations de la variance et peut rejeter le code source bancal au moment de la compilation au lieu de lancer des exceptions à l'exécution, alors que les fichiers .class résultant peut être utilisé de manière transparente à partir de Java.

L'un des inconvénients de la variance du site de déclaration est qu'il semble faire l'inférence de type plus difficile dans certains cas.

En même temps Scala peut utiliser des types primitifs, sans les boxe dans les collections comme en C # en utilisant l'annotation @specialized qui indique au compilateur Scala pour générer une ou plusieurs implémentations supplémentaires d'une classe ou d'une méthode spécialisée au demandé type primitif.

Scala peut aussi « presque » réifier génériques en utilisant Manifestes ce qui leur permet de récupérer les types génériques à l'exécution comme dans C #.

Inconvénients des génériques de style Java

Une conséquence est que la version java fonctionne uniquement avec les types de référence (ou types de valeur) et non en boîte types de valeur. OMI qui est le plus grand inconvénient car il empêche les génériques de haute performance dans un grand nombre de scénarios et nécessite l'écriture manuelle de types spécialisés.

Il ne garantit pas un invariant comme « Cette liste ne contient que des objets de type x » et les besoins runtime contrôles à chaque getter. Le type générique existe vraiment.

Lors de l'utilisation réflexion vous ne pouvez pas demander à une instance d'un objet générique qui paramètres générique, il a.

Les avantages des génériques de style Java

Vous obtenez la variance / peut jeter entre les différents paramètres génériques.

Java: génériques de variance-utilisation du site depuis Java 5. Cassé tableaux covariantes avec une syntaxe différente depuis la version 1.0. Pas d'info type d'exécution des génériques.

C #: génériques de variance-utilisation du site depuis C # 2.0. variance du site de déclaration ajoutée en C # 4.0. Cassé tableaux covariantes avec une syntaxe différente depuis la version 1.0 (problème identique à Java). « Réifiées » génériques qui signifie que les informations de type ne soit pas perdu au moment de la compilation.

Scala: deux utilisent site / variance déclaration de site depuis les premières versions de la langue (au moins depuis 2008). Les tableaux ne sont pas une caractéristique de langue distincte, de sorte que vous utilisez les mêmes génériques syntaxe et les règles de la variance de type. Certaines collections sont mises en œuvre au niveau VM avec des tableaux JVM afin que vous obteniez performance égale ou supérieure d'exécution par rapport avec le code Java.

Pour élaborer le C # / Java problème de sécurité de type tableau: Vous pouvez jeter un chien [] pour un animal de compagnie [] et ajouter un chat et déclencher une erreur d'exécution qui ne sont pas pris au moment de la compilation. Scala a mis en œuvre cette correctement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top