web-dev-qa-db-fra.com

La méthode a le même effacement qu'une autre méthode du type

Pourquoi n'est-il pas légal d'avoir ces deux méthodes 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 le même effacement add (Set) qu'une autre méthode du type Test.

pendant que je pouvais y remédier, je me demandais pourquoi javac n’aime pas cela.

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){}

méthode, mais ce n'est pas toujours le cas.

C’est très ennuyeux si vous voulez avoir deux constructors qui prennent ces arguments car vous ne pouvez pas simplement changer le nom de l’un des constructors.

348
Omry Yadan

Cette règle est destinée à éviter les conflits dans le code existant qui utilise toujours des types bruts.

Voici une illustration de la raison pour laquelle cela n'a pas été autorisé, tiré du JLS. Supposons, avant que les génériques ne soient introduits à Java, j'ai écrit du code comme celui-ci:

class CollectionConverter {
  List toList(Collection c) {...}
}

Vous prolongez ma classe, comme ceci:

class Overrider extends CollectionConverter{
  List toList(Collection c) {...}
}

Après l’introduction des génériques, j’ai décidé de mettre à jour ma bibliothèque.

class CollectionConverter {
  <T> List<T> toList(Collection<T> c) {...}
}

Vous n'êtes pas prêt à faire des mises à jour, vous laissez donc votre classe Overrider seule. Afin de remplacer correctement la méthode toList(), les concepteurs de langage ont décidé qu'un type brut était "équivalent à une substitution" à tout type généré. Cela signifie que bien que votre signature de méthode ne soit plus formellement égale à celle de ma super-classe, 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 bousillez un peu, et au lieu d’éditer la méthode brute toList() existante, vous ajoutez une nouvelle méthode comme celle-ci:

class Overrider extends CollectionConverter {
  @Override
  List toList(Collection c) {...}
  @Override
  <T> List<T> toList(Collection<T> c) {...}
}

En raison de l'équivalence de substitution des types bruts, les deux méthodes sont sous une forme valide pour remplacer la méthode toList(Collection<T>). 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 à utiliser 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 la méthode se produit au moment de la compilation, l'ajout de types génériques à l'identificateur de la méthode aurait été suffisant.

334
erickson

Les génériques Java utilisent l’effacement de type. Le bit entre crochets (<Integer> et <String>) est supprimé. Vous obtenez donc deux méthodes ayant une signature identique (la add(Set) que vous voyez dans l'erreur). Cela n'est pas autorisé car le moteur d'exécution 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 à présent.

106
GaryF

En effet, Java Les génériques sont implémentés avec Type Erasure .

Vos méthodes seraient traduites, au moment de la compilation, en quelque chose comme:

La résolution de la méthode se produit à la compilation et ne prend pas en compte les paramètres de type. ( voir la réponse d'Erickson )

void add(Set ii);
void add(Set ss);

Les deux méthodes ont la même signature sans les paramètres de type, d'où l'erreur.

41
bruno conde

Le problème est que Set<Integer> et Set<String> sont en réalité traités comme un Set de la machine virtuelle. La sélection d'un type pour l'ensemble (String ou Integer dans votre cas) n'est que du sucre syntaxique utilisé par le compilateur. La JVM ne peut pas distinguer Set<String> de Set<Integer>.

20
kgiannakakis

Définir une méthode unique sans type comme void add(Set ii){}

Vous pouvez mentionner le type lorsque vous appelez la méthode en fonction de votre choix. Cela fonctionnera pour tout type d'ensemble.

5
Idrees Ashraf

Il est possible que le compilateur traduise Set (Integer) en Set (Object) dans le code d'octet Java. Si tel est le cas, Set (Integer) sera utilisé uniquement lors de la phase de compilation pour la vérification de la syntaxe.

3
rossoft