44 votes

Pourquoi les méthodes génériques et les types génériques ont une syntaxe d’introduction de type différent ?

Tout en étudiant les génériques, j'ai remarqué une différence dans le type de l'introduction de la syntaxe entre les méthodes génériques et les types génériques (de la classe ou de l'interface) qui m'ont troublé.

La syntaxe d'une méthode générique est

<T> void doStuff(T t) {
    // Do stuff with T
}

Les docs disent

La syntaxe d'une méthode générique comprend un paramètre de type, à l'intérieur des crochets, et apparaît avant le retour de la méthode type de

La syntaxe pour un type générique est

class Stuff<T> {
    // Do stuff with T
    T t;
}

Les docs disent

Le paramètre de type de section, délimitée par des crochets (<>), suit le nom de la classe. Il spécifie les paramètres de type

Ni elle explique pourquoi elle doit venir avant ou après.


Afin d'être cohérents les uns avec les autres, je m'attendais soit la méthode de syntaxe
void doStuff<T>(T t) {}ou le type de syntaxe (pour la classe) pour être class <T>Stuff {}, mais ce n'est évidemment pas le cas.

Pourquoi doit-on avoir pour être introduite avant et l'autre après?

J'ai utilisé les génériques, principalement sous forme d' List<String> et fait valoir que l' <String>List peut sembler étrange, mais c'est un argument subjectif, en outre, pour les méthodes, il est ainsi. Vous pouvez appeler doStuff comme this.<String>doStuff("a string");

La recherche d'une explication technique, je pensais peut-être, <T> doit être présenté à une méthode avant de spécifier le type de retour, car T pourrait être le type de retour et le compilateur n'est peut-être pas en mesure de regarder à l'avance comme ça, mais c'était étrange parce que les compilateurs sont intelligents.

Je l'ai trouver il y a une explication pour cela, au-delà de "la langue de designers vient de fait de cette façon", mais je ne pouvais pas le trouver.

19voto

frant.hartm Points 4634

La réponse se trouve en effet dans le GJ Spécification, qui a déjà été lié, citation du document, p.14:

La convention de passage de paramètres avant le nom de la méthode est rendue nécessaire par l'analyse de contraintes: les plus classiques de type paramètres après le nom de la méthode" de la convention de l'expression f (a<b,c>(d)) serait possible de la traite.

La mise en œuvre des commentaires:

f(a<b,c>(d)) peut analysée comme une des f(a < b, c > d) (deux booléens à partir de comparaisons passé à f) ou f(a<B, C>(d)) (appel d'un avec des arguments de type B et C et la valeur de l'argument passé d à f). Je pense que cela pourrait aussi être la raison pour laquelle Scala choisi d'utiliser [] au lieu de <> pour les génériques.

10voto

Radu Dumbrăveanu Points 795

Autant que je sache generics de Java, quand ils ont été introduits, ont été fondés sur l'idée de génériques de GJ (une extension du langage de programmation Java qui prend en charge les types génériques). Par conséquent, la syntaxe a été prise à partir de GJ, voir GJ Spécification.

C'est un véritable réponse à votre question, mais pas de réponse à votre question dans le contexte de la GJ. Mais il est clair qu'il n'a rien à voir avec la syntaxe C++ parce que en C++ paramètre de la section qui précède les deux class mot-clé et le type de retour de la méthode.

8voto

daniu Points 8648

Mon hypothèse est que c'est parce que comme vous l'avez dit pour une méthode, le paramètre générique peut également être le type de retour d'une fonction:

public <RETURN_TYPE> RETURN_TYPE getResult();

Donc, au moment où le compilateur arrive sur le type de retour de la fonction, c'est le type a déjà été rencontré (comme dans, il sait que c'est un type générique).

Si vous avez eu une syntaxe comme

public RETURN_TYPE getResult<RETURN_TYPE>();

elle aurait besoin d'un deuxième balayage à analyser.

Pour les classes, ce n'est pas un problème, parce que toutes les références pour le type générique apparaissent dans la définition de la classe de bloc, c'est à dire après le générique de type a été déclarée.

5voto

EJoshuaS Points 7022

Il n'y a pas une profonde raison théorique pour ce qui semble être un cas de "la langue des designers ai fait de cette façon." C#, par exemple, n' utilisent exactement la syntaxe que vous vous demandez pourquoi Java n'est pas mise en œuvre. Le code suivant:

private T Test<T>(T abc)
{
    throw new NotImplementedException();
}

compiler. C# est assez similaire à Java que cela impliquerait qu'il n'y a pas de raison théorique que Java ne pouvait pas avoir mis en œuvre la même chose, trop (surtout étant donné que les deux langues mises en œuvre génériques dès le début de leur développement).

L'avantage de la syntaxe Java comme il est maintenant, c'est qu'il est légèrement plus facile à mettre en œuvre un LL(1) analyseur pour les méthodes utilisant la syntaxe.

4voto

OLIVER.KOO Points 2903

La raison est que le type générique et paramétrable type sont traitées différemment lors de la compilation. L'un est de voir que Eliding les paramètres de type et de l'autre est Eliding arguments de type pendant le processus d'effacement.

Les génériques est ajoutée à Java, en 2004, au sein de la version officielle de J2SE 5.0. DANS un Oracle de la documentation "à l'Aide et à la Programmation des Génériques dans J2SE 5.0", a déclaré

Derrière les Coulisses

Les génériques sont mis en œuvre par le compilateur Java front-end conversion appelé effacement, qui est le processus de la traduction ou de la réécriture du code qui utilise les génériques et non génériques de code (qui est, les cartes la nouvelle syntaxe pour le courant de la JVM de spécification). En d'autres termes, ce conversion efface tous les génériques des informations de type; toutes les informations entre crochets est effacé. Par exemple, LinkedList deviendra LinkedList. Les utilisations de l'autre type, les variables sont remplacées par la limite supérieure de la variable type (par exemple un Objet), et quand le code résultant est pas le type correct, un cast vers le type approprié est inséré.

La clé est dans le processus de Type Erasure. Pas de JVM des modifications ont été apportées à l'appui de médicaments génériques afin de Java ne pas se souvenir de type générique passé de compilation.

Dans ce que l'on appelle le Coût De l'Effacement pubslished par l'Université de la Nouvelle-Orléans a brisé les étapes d'Effacement pour nous:

Les mesures effectuées au cours de type effacement comprennent:

  • Eliding paramètres de type : Lorsque le compilateur trouve la définition d'un type générique ou de la méthode, il supprime toutes les occurrences de chaque type paramètre de le remplacer par son la plus à gauche lié, ou d'un Objet si aucune limite est spécifié.

  • Eliding arguments de type: Lorsque le compilateur trouve un type paramétré, une instanciation d'un type générique, il supprime le type de les arguments. Par exemple, le type List<String> est traduite en List.

Pour la méthode générique le compilateur est à la recherche pour le générique de la définition de type qui est à gauche les plus liés. Et il signifie littéralement en partant de la gauche et c'est pourquoi la Délimitée Tapé Paramètres s'affiche avant le retour de la méthode type Pour le générique de la classe ou de l'interface, le compilateur est à la recherche d'paramétrée type qui, à la différence de type générique, il n'est pas le plus à gauche de la borne de la définition de la classe, mais au lieu de cela suit celui de la classe. Le compilateur puis supprime le type d'arguments JVM peut le comprendre.

Si vous extrayez la section Annexe de Coût De l'Effacement de papier. Bien démontrer comment le compilateur gère les génériques de l'interface et des méthodes.

Pont Méthodes

Lors de la compilation d'une classe ou d'une interface qui étend une classe paramétrée ou implémente une interface paramétrable, le compilateur peut-être besoin d' créer une méthode synthétique, appelé pont de la méthode, dans le cadre de la type le processus d'effacement. Normalement, vous n'avez pas besoin de vous soucier de pont méthodes, mais vous pourriez être surpris si l'un apparaît dans une trace de la pile.

Remarque: En outre, le compilateur peut parfois besoin d'inserts synthétiques pont méthodes. Pont Méthodes est partie du type de processus d'effacement. Pont méthodes est responsable de s'assurer que les signatures de méthodes de match après l'effacement. Lire plus à ce sujet à des Effets de Type d'Effacement et de Pont Méthodes


Edit: Comme OP rappelle ma conclusion de "la plus à gauche liée" signifie littéralement les moyens les plus à gauche n'est pas assez solide. (OP a fait état dans sa question qu'il n'est pas intéressé par "je pense", le type de réponse) donc j'ai fait un peu de creuser et trouvé ce GenericsFAQ. Dans l'exemple, il semble que l'ordre des paramètres de type a de l'importance. c'est à dire <T extends Cloneable & Comparable<T>> devient Cloneable après enrasure mais pas Comparable

enter image description here

voici un autre exemple directement auprès de Oracle Effacement de Type Générique

Dans l'exemple suivant, le générique de la classe de Nœud utilise un délimitée type de paramètre:

public class Node<T extends Comparable<T>> {
   ...
}

Le compilateur Java remplace le délimitée paramètre de type T avec la première classe liée, Comparable.

Je pense que le plus techniquement correcte est-à-dire le type d'effacement de remplacer le type avec la première classe liée (ou Object si T est illimitée) il se trouve que la première classe liée est le plus à gauche tenus à cause de la syntaxe de Java.

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