Pourquoi n'est-il pas légal d'avoir les deux méthodes suivantes dans la même classe?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
Je reçois le compilation error
La méthode add (Set) a la même fonction d'effacement add (Set) qu'une autre méthode de type Test.
alors que je peux contourner cela, je me demandais pourquoi javac n'aime pas ça.
Je peux voir que dans de nombreux cas, la logique de ces deux méthodes serait très similaire et pourrait être remplacée par une seule
public void add(Set<?> set){}
mais ce n'est pas toujours le cas.
C'est très ennuyeux si vous voulez en avoir deux constructors
qui prennent ces arguments car vous ne pouvez pas simplement changer le nom de l'un des constructors
.
List
et je ne sais pas comment y faire face.Réponses:
Cette règle vise à éviter les conflits dans le code hérité qui utilise toujours des types bruts.
Voici une illustration de la raison pour laquelle cela n'a pas été autorisé, tirée du JLS. Supposons que, avant l'introduction des génériques en Java, j'ai écrit du code comme celui-ci:
Vous prolongez ma classe, comme ceci:
Après l'introduction des génériques, j'ai décidé de mettre à jour ma bibliothèque.
Vous n'êtes pas prêt à faire des mises à jour, alors vous laissez votre
Overrider
classe tranquille. Afin de remplacer correctement latoList()
méthode, les concepteurs de langage ont décidé qu'un type brut était "équivalent à la substitution" à tout type généré. Cela signifie que bien que la signature de votre méthode ne soit plus formellement égale à la signature de ma superclasse, votre méthode est toujours prioritaire.Maintenant, le temps passe et vous décidez que vous êtes prêt à mettre à jour votre classe. Mais vous bousiller un peu, et au lieu de modifier la
toList()
méthode brute existante , vous ajoutez une nouvelle méthode comme celle-ci:En raison de l'équivalence de remplacement des types bruts, les deux méthodes sont sous une forme valide pour remplacer la
toList(Collection<T>)
méthode. Mais bien sûr, le compilateur doit résoudre une seule méthode. Pour éliminer cette ambiguïté, les classes ne sont pas autorisées à avoir plusieurs méthodes équivalentes à la substitution, c'est-à-dire plusieurs méthodes avec les mêmes types de paramètres après l'effacement.La clé est qu'il s'agit d'une règle de langage conçue pour maintenir la compatibilité avec l'ancien code à l'aide de types bruts. Ce n'est pas une limitation requise par l'effacement des paramètres de type; étant donné que la résolution de méthode se produit au moment de la compilation, l'ajout de types génériques à l'identificateur de méthode aurait été suffisant.
la source
javac
.List<?>
et certainsenum
que vous définissez pour indiquer le type. La logique spécifique au type pourrait en fait être dans une méthode sur l'énumération. Alternativement, vous souhaiterez peut-être créer deux classes différentes, plutôt qu'une classe avec deux constructeurs différents. La logique commune serait dans une superclasse ou un objet d'assistance auquel les deux types délèguent.Les génériques Java utilisent l'effacement des types. Le bit entre les crochets (
<Integer>
et<String>
) est supprimé, vous vous retrouveriez donc avec deux méthodes qui ont une signature identique (celle queadd(Set)
vous voyez dans l'erreur). Ce n'est pas autorisé car le runtime ne sait pas lequel utiliser pour chaque cas.Si Java obtient des génériques réifiés, vous pouvez le faire, mais c'est probablement peu probable maintenant.
la source
En effet, Java Generics est implémenté avec Type Erasure .
Vos méthodes seraient traduites, au moment de la compilation, en quelque chose comme:La résolution de méthode se produit au moment de la compilation et ne prend pas en compte les paramètres de type. ( voir la réponse d'Erickson )
Les deux méthodes ont la même signature sans les paramètres de type, d'où l'erreur.
la source
Le problème est que
Set<Integer>
etSet<String>
sont en fait traités comme unSet
de la JVM. La sélection d'un type pour l'ensemble (chaîne ou entier dans votre cas) n'est que du sucre syntaxique utilisé par le compilateur. La JVM ne peut pas distinguer entreSet<String>
etSet<Integer>
.la source
Set
, mais comme la résolution de la méthode se produit au moment de la compilation, lorsque les informations nécessaires sont disponibles, cela n'est pas pertinent. Le problème est qu'autoriser ces surcharges entrerait en conflit avec la tolérance pour les types bruts, ils ont donc été rendus illégaux dans la syntaxe Java.(Ljava/util/Collection;)Ljava/util/List;
cela ne fonctionne pas. Vous pouvez utiliser(Ljava/util/Collection<String>;)Ljava/util/List<String>;
, mais c'est un changement incompatible et vous rencontreriez des problèmes insolubles dans des endroits où tout ce que vous avez est un type effacé. Vous devrez probablement supprimer complètement l'effacement, mais c'est assez compliqué.Définissez une seule méthode sans type comme
void add(Set ii){}
Vous pouvez mentionner le type lors de l'appel de la méthode en fonction de votre choix. Cela fonctionnera pour tout type de jeu.
la source
Il est possible que le compilateur traduise Set (Integer) en Set (Object) en code d'octet java. Si tel est le cas, Set (Integer) ne sera utilisé qu'à la phase de compilation pour la vérification de la syntaxe.
la source
Set
. Les génériques n'existent pas dans le code octet, ils sont du sucre syntaxique pour la conversion et offrent une sécurité de type au moment de la compilation.Je suis tombé sur cela quand j'ai essayé d'écrire quelque chose comme:
Continuable<T> callAsync(Callable<T> code) {....}
etContinuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
ils deviennent pour le compilateur les 2 définitions deContinuable<> callAsync(Callable<> veryAsyncCode) {...}
L'effacement de type signifie littéralement l'effacement des informations d'arguments de type des génériques. C'est TRÈS ennuyeux, mais c'est une limitation qui sera avec Java pendant un certain temps. Pour les constructeurs, pas grand chose à faire, 2 nouvelles sous-classes spécialisées avec différents paramètres en constructeur par exemple. Ou utilisez plutôt des méthodes d'initialisation ... (constructeurs virtuels?) Avec des noms différents ...
pour des méthodes de fonctionnement similaires, renommer aiderait, comme
Ou avec des noms plus descriptifs, auto-documentés pour les cas oyu, comme
addNames
etaddIndexes
ou tels.la source