89 votes

Qu'est-ce qui est cool avec les génériques, pourquoi les utiliser?

Je pensais offrir cette balle à quiconque voudrait la sortir du parc. Quels sont les génériques, quels sont les avantages des génériques, pourquoi, où, comment dois-je les utiliser? S'il vous plaît, gardez-le assez basique. Merci.

128voto

ljs Points 16511
  • Vous permet d'écrire du code, de l'utilisation de la bibliothèque des méthodes qui sont de type-safe, c'est à dire une List<string> est la garantie d'une liste de chaînes de caractères.
  • Comme un résultat de génériques utilisé le compilateur peut effectuer des contrôles de compilation de code de sécurité de type, c'est à dire que vous essayez de mettre un int dans la liste de chaînes de caractères? À l'aide d'une liste de tableaux ferais en sorte que le moins transparent erreur d'exécution.
  • Plus rapide que l'utilisation des objets qu'il soit évite boxing/unboxing (où .net a pour convertir des types de valeur pour les types référence, ou vice-versa) ou de moulage à partir d'objets de la référence type.
  • Vous permet d'écrire du code qui est applicable à de nombreux types avec les mêmes sous-jacents au comportement, à savoir un Dictionary<string, int> utilise le même code sous-jacent comme un Dictionnaire<DateTime, double>; l'utilisation de génériques, le cadre de l'équipe n'avait qu'à écrire un bout de code pour obtenir des résultats avec les mêmes avantages.

53voto

James Schek Points 11070

J'ai vraiment hate de me répéter. Je déteste taper la même chose plus souvent que je le dois. Je n'aime pas retraiter les choses plusieurs fois avec de légères différences.

Au lieu de créer:

class MyObjectList  {
   MyObjectget(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObjectget(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

Je peux construire une classe réutilisable... (dans le cas où vous ne souhaitez pas utiliser le raw collection pour une certaine raison)

class MyList<T> {
   T get(int index) { ... }
}

Je suis maintenant 3x plus efficace et je n'ai à conserver une copie. Pourquoi ne PAS vous souhaitez garder le moins de code?

Cela est également vrai pour les non-classes de collection comme un Callable<T> ou Reference<T> qui a pour interagir avec d'autres classes. Voulez-vous vraiment à étendre Callable<T> et Future<T> et tous les autres associés de classe pour créer des type-safe versions?

Je n'ai pas.

22voto

coobird Points 70356

N'ayant pas besoin de transtypage est l'un des plus grands avantages de Java génériques, comme il va effectuer une vérification de type à la compilation. Cela permettra de réduire la possibilité d' ClassCastExceptions qui peut être levée lors de l'exécution, et peut conduire à code plus robuste.

Mais je soupçonne que vous êtes pleinement conscient de cela.

Chaque fois que je regarde les Génériques il donne moi un mal de tête. Je trouve que la meilleure partie de Java c'est de la simplicité et un minimum de la syntaxe et les génériques ne sont pas simples et ajouter une quantité importante de nouveaux la syntaxe.

D'abord, je ne vois pas l'avantage des génériques. J'ai commencé à apprendre le Java à partir de la 1.4 syntaxe (même si Java 5 était sorti à l'époque) et quand j'ai rencontré les génériques, j'ai senti que c'était plus de code à écrire, et je ne comprend pas vraiment les avantages.

Les Ide modernes font de l'écriture de code avec les génériques plus facile.

Plus moderne, la plus décent IDEs sont assez intelligents pour aider à l'écriture de code avec les médicaments génériques, en particulier avec la complétion de code.

Voici un exemple de faire une Map<String, Integer> avec un HashMap. Le code que j'aurais à taper est:

Map<String, Integer> m = new HashMap<String, Integer>();

Et en effet, c'est beaucoup pour le type qui vient de faire une nouvelle HashMap. Cependant, dans la réalité, je n'avais qu'à taper ce bien avant l'Éclipse savais ce que je devais:

Map<String, Integer> m = new Ha Ctrl+Espace

Vrai, je n'ai besoin de sélectionner HashMap à partir d'une liste de candidats, mais, fondamentalement, l'IDE savait quoi ajouter, y compris les types génériques. Avec les bons outils, l'utilisation de génériques n'est pas trop mauvais.

En outre, depuis les types sont connus, lors de la récupération des éléments de la collection générique, l'IDE va agir comme si l'objet est déjà un objet de son type déclaré -- il n'y a pas besoin de casting pour l'IDE pour savoir ce que le type de l'objet.

Un avantage clé de génériques vient de la manière dont il joue bien avec Java 5 caractéristiques. Voici un exemple de jeter des entiers dans un Set et le calcul de sa totale:

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

Dans ce morceau de code, il y a trois nouvelles Java 5 caractéristiques:

Tout d'abord, de génériques et de l'autoboxing de primitives permettent les lignes suivantes:

set.add(10);
set.add(42);

L'entier 10 est autoboxed en Integer de la valeur de 10. (Et même pour 42). Alors qu' Integer est lancée dans l' Set qui est connu pour détenir Integers. En essayant de jeter un String serait la cause d'une erreur de compilation.

Ensuite, pour la boucle for-each prend tous les trois de ceux-ci:

for (int i : set) {
  total += i;
}

Tout d'abord, l' Set contenant Integers sont utilisés dans une boucle for-each. Chaque élément est déclarée int et qui est admis que l' Integer est unboxed retour à la primitive int. Et le fait que cet unboxing se produit est connu, parce que les génériques a été utilisé pour spécifier qu'il n'y avait Integers est tenue dans l' Set.

Les génériques peuvent être le ciment qui réunit les nouvelles fonctionnalités introduites dans Java 5, et il rend la programmation plus simple et plus sûre. Et la plupart du temps, les Ide sont assez intelligents pour vous aider avec de bonnes suggestions, donc, généralement, il ne sera pas beaucoup plus à taper.

Et franchement, comme peut être vu à partir de l' Set exemple, je sens que l'utilisation de Java 5 fonctionnalités peut rendre le code plus concis et robuste.

Edit - Un exemple sans les génériques

Ce qui suit est une illustration de ce qui précède Set exemple sans l'utilisation de médicaments génériques. C'est possible, mais n'est pas exactement agréable:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(Note: Le code ci-dessus va générer unchecked conversion d'avertissement lors de la compilation.)

Lors de l'utilisation de la non-génériques collections, les types qui sont entrées dans la collection des objets de type Object. Par conséquent, dans cet exemple, un Object est ce qui est added dans l'ensemble.

set.add(10);
set.add(42);

Dans les lignes ci-dessus, l'autoboxing est en jeu: la primitive int valeur 10 et 42 sont autoboxed en Integer objets, qui sont ajoutés à l' Set. Cependant, gardez à l'esprit, l' Integer des objets sont manipulés comme Objects, comme il n'y a pas de type d'informations pour aider le compilateur de savoir quel type de l' Set devrait s'attendre.

for (Object o : set) {

C'est la partie qui est cruciale. La raison pour laquelle la boucle for-each fonctionne est parce que l' Set implémente l' Iterable interface, qui retourne un Iterator avec des informations de type, s'il est présent. (Iterator<T>, ce qui est.)

Cependant, puisqu'il n'est pas le type de l'information, l' Set sera de retour un Iterator qui permet de renvoyer les valeurs dans l' Set comme Objects, et c'est pourquoi l'élément en cours de récupération pour chaque boucle doit être de type Object.

Maintenant que l' Object est récupéré à partir de l' Set, il doit être converti dans un Integer manuellement pour effectuer l'addition:

  total += (Integer)o;

Ici, un transtypage est effectuée à partir d'une Object d'un Integer. Dans ce cas, nous savons que cela fonctionnera toujours, mais manuel typecasting me fait toujours sentir qu'il est fragile du code qui pourrait être endommagé si un changement mineur est faite ailleurs. (J'ai l'impression que tous les transtypage est un ClassCastException en attente de se produire, mais je m'égare...)

L' Integer est maintenant unboxed en int et autorisé à effectuer le plus dans l' int variable total.

J'espère que je pourrais illustrer le fait que les nouvelles fonctionnalités de Java 5 est possible d'utiliser avec code non générique, mais il n'est tout simplement pas aussi propre et simple que l'écriture de code avec les génériques. Et, à mon avis, de tirer pleinement parti des nouvelles fonctionnalités de Java 5, on devrait être à la recherche dans les génériques, si à tout le moins, permet de compiler des contrôles pour prévenir les invalides typecasts lancer des exceptions à l'exécution.

15voto

Si vous avez été à la recherche du bug de Java de base de données juste avant 1.5 a été publié, vous trouverez sept fois plus de bugs avec NullPointerException que ClassCastException. Donc, il ne semble pas qu'il est une grande fonctionnalité pour trouver des bogues, ou au moins de bugs qui persistent après un petit test de détection de fumée.

Pour moi l'énorme avantage de génériques, c'est qu'ils document dans le code type d'information. Si je ne voulais pas que ce type d'informations enregistrées dans le code, alors je vais utiliser un typées dynamiquement la langue, ou au moins une langue avec plus implicite que l'inférence de type.

En gardant un objet des collections à lui-même n'est pas un mauvais style (mais alors le bon style, est effectivement de les ignorer l'encapsulation). Il s'appuie plutôt sur ce que vous faites. En passant collections à des "algorithmes" est un peu plus facile à vérifier (au plus tard au moment de la compilation) avec les génériques.

11voto

Apocalisp Points 22526

Les médicaments génériques en Java faciliter le polymorphisme paramétrique. Par le biais de paramètres de type, vous pouvez passer des arguments de types. Tout comme une méthode comme String foo(String s) modèles de certains comportements, pas seulement pour une chaîne particulière, mais pour n'importe quelle chaîne de caractères s, de sorte qu'un type comme List<T> modèles de certains comportements, pas seulement pour un type spécifique, mais pour n'importe quel type. List<T> dit que pour n'importe quel type d' T, il y a un type d' List dont les éléments sont Ts. Donc, List est en fait un constructeur de type. Il faut un type comme argument et des constructions d'un autre type.

Voici quelques exemples de types génériques que j'utilise tous les jours. Tout d'abord, un très utile interface générique:

public interface F<A, B> {
  public B f(A a);
}

Cette interface est dit que pour certains deux types, A et B, il existe une fonction (appelée f) qui prend un A et renvoie un B. Lors de l'implémentation de cette interface, A et B peut être tous les types que vous voulez, tant que vous fournissez une fonction f qui prend l'ancien et retourne celui-ci. Voici un exemple d'implémentation de l'interface:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

Avant de génériques, le polymorphisme a été réalisée par le sous-classement à l'aide de l' extends mot-clé. Avec les génériques, on peut effectivement faire disparaître sous-classement et l'utilisation du polymorphisme paramétrique à la place. Par exemple, considérons une paramétré (générique) de la classe utilisée pour calculer les codes de hachage pour n'importe quel type. La place primordiale à l'Objet.hashCode(), nous utilisons une classe générique comme ceci:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

C'est beaucoup plus souple que l'utilisation de l'héritage, parce que nous pouvons rester avec le thème de l'utilisation de la composition et de polymorphisme paramétrique sans verrouillage fragile hiérarchies.

Java les génériques ne sont pas parfait, mais. Vous pouvez résumé par types, mais vous ne pouvez pas vous soustraire plus de type de constructeurs, par exemple. Qui est, vous pouvez dire "pour tout type T", mais vous ne pouvez pas dire "pour tout type T qui prend un paramètre de type".

J'ai écrit un article à propos de ces limites de Java génériques, ici.

Une grande victoire avec les génériques, c'est qu'ils vous permettent d'éviter de sous-classement. Sous-classement a tendance à se traduire en cassant les hiérarchies de classe qui sont difficiles à étendre, et les classes qui sont difficiles à comprendre individuellement sans avoir à regarder l'ensemble de la hiérarchie.

Alors que avant les génériques que vous pourriez avoir des classes comme l' Widget prolongée par FooWidget, BarWidget, et BazWidget, avec les médicaments génériques, vous pouvez avoir une seule classe générique Widget<A> qui prend un Foo, Bar ou Baz dans son constructeur afin de vous donner des Widget<Foo>, Widget<Bar>, et Widget<Baz>.

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