Qu'est-ce qui est cool à propos des génériques, pourquoi les utiliser?
Je pensais offrir ce softball à qui voudrait le frapper hors du parc. Ce sont des génériques, quels sont les avantages des médicaments génériques, pourquoi, où, comment dois-je utiliser? Je vous en prie, gardez-le assez basique. Grâce.
28 réponses
- vous permet d'écrire du code/d'utiliser des méthodes de bibliothèque qui sont sûres, c'est-à-dire Qu'une liste
est garantie comme une liste de chaînes. - à la suite de l'utilisation de génériques, le compilateur peut effectuer des contrôles de compilation sur le code pour la sécurité des types, c'est-à-dire essayez-vous de mettre un int dans cette liste de chaînes? L'utilisation D'une ArrayList entraînerait une erreur d'exécution moins transparente.
- plus rapide que d'utiliser des objets car il évite la boxe/unboxing (où. net doit convertir types de valeur aux types de référence ou vice-versa ) ou conversion d'objets vers le type de référence requis.
- vous permet d'écrire du code qui est applicable à de nombreux types avec le même comportement sous-jacent, c'est-à-dire un dictionnaire
utilise le même code sous-jacent qu'un dictionnaire ; en utilisant des génériques, l'équipe du framework n'a dû écrire qu'un seul morceau de code pour obtenir les deux
Je déteste vraiment me répéter. Je déteste taper la même chose plus souvent que je ne le dois. Je n'aime pas répéter les choses plusieurs fois avec de légères différences.
Au Lieu de créer:
class MyObjectList {
MyObject get(int index) {...}
}
class MyOtherObjectList {
MyOtherObject get(int index) {...}
}
class AnotherObjectList {
AnotherObject get(int index) {...}
}
, je peux construire une classe réutilisable... (dans le cas où vous ne voulez pas utiliser la collection raw pour une raison quelconque)
class MyList<T> {
T get(int index) { ... }
}
Je suis maintenant 3 fois plus efficace et je n'ai qu'à en conserver une copie. Pourquoi ne voudriez-vous pas maintenir moins de code?
Ceci est également vrai pour classes non-collection telles qu'un Callable<T>
ou un Reference<T>
qui doit interagir avec d'autres classes. Voulez-vous vraiment étendre Callable<T>
et Future<T>
et toutes les autres classes associées pour créer des versions sûres?
Pas moi.
Ne pas avoir besoin de typecast est l'un des plus grands avantages des génériques Java, car il effectuera une vérification de type au moment de la compilation. Cela réduira la possibilité de ClassCastException
s qui peuvent être lancés à l'exécution, et peut conduire à un code plus robuste.
Mais je soupçonne que vous êtes pleinement conscient de cela.
Chaque fois que je regarde les génériques, cela donne moi un mal de tête. Je trouve la meilleure partie de Java pour être c'est la simplicité et minimal la syntaxe et les génériques ne sont pas simples et ajouter une quantité importante de nouveaux syntaxe.
Au début, je ne voyais pas non plus l'avantage des génériques. J'ai commencé à apprendre Java à partir de la syntaxe 1.4 (même si Java 5 était sorti à l'époque) et quand j'ai rencontré des génériques, j'ai senti que c'était plus de code à écrire, et je ne comprenais vraiment pas les avantages.
Les IDE modernes facilitent l'écriture de code avec des génériques.
La plupart des IDE modernes et décents sont assez intelligents pour aider à écrire du code avec des génériques, surtout avec l'achèvement du code.
Voici un exemple de faire un Map<String, Integer>
avec HashMap
. Le code que je devrais taper est:
Map<String, Integer> m = new HashMap<String, Integer>();
Et en effet, c'est beaucoup à taper juste pour faire un nouveau HashMap
. Cependant, en réalité, je n'avais qu'à taper autant avant Qu'Eclipse ne sache ce dont j'avais besoin:
Map<String, Integer> m = new Ha
Ctrl+L'Espace
Certes, j'avais besoin de sélectionner HashMap
dans une liste de candidats, mais fondamentalement l'IDE savait quoi ajouter, y compris les types génériques. Avec les bons outils, utiliser des génériques n'est pas trop mal.
De plus, puisque les types sont connus, lors de la récupération d'éléments de la collection générique, L'IDE agira comme si cet objet était déjà un objet de son type déclaré-Il n'est pas nécessaire de lancer pour que L'IDE sache quel est le type de l'objet.
Un avantage clé des génériques vient de la façon dont il joue bien avec les nouvelles fonctionnalités de Java 5. voici un exemple de lancer des entiers dans un Set
et de calculer son total:
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 fonctionnalités Java 5 présentes:
Tout d'abord, les génériques et l'autoboxing des primitives permettent les lignes suivantes:
set.add(10);
set.add(42);
L'entier 10
est autoboxé dans un Integer
avec la valeur de 10
. (Et même pour 42
). Alors que Integer
est jeté dans le Set
qui est connu pour contenir Integer
s. essayer de lancer un String
provoquerait une erreur de compilation.
Ensuite, for for-chaque boucle prend les trois:
for (int i : set) {
total += i;
}
Tout d'abord, les Set
contenant Integer
sont utilisés dans une boucle for-each. Chaque élément est déclaré int
et cela est autorisé car le {[16] } est décoché vers la primitive int
. Et le fait que ce déballage se produise est connu parce que generics a été utilisé pour spécifier qu'il y avait des Integer
s détenus dans le Set
.
Génériques peut être la colle qui rassemble les nouvelles fonctionnalités introduites dans Java 5, et cela rend le codage plus simple et plus sûr. 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 de frappe.
Et franchement, comme on peut le voir dans l'exemple Set
, je pense que l'utilisation des fonctionnalités Java 5 peut rendre le code plus concis et plus robuste.
Edit - Un exemple sans les génériques
Ce qui suit est une illustration de l'exemple Set
ci-dessus sans l'utilisation de génériques. C'est possible, mais ce n'est pas vraiment agréable:
Set set = new HashSet();
set.add(10);
set.add(42);
int total = 0;
for (Object o : set) {
total += (Integer)o;
}
(remarque: le code ci-dessus générera un avertissement de conversion non coché au moment de la compilation.)
Lors de l'utilisation de collections non génériques, les types entrés dans la collection sont des objets de type Object
. Par conséquent, dans cet exemple, un Object
est ce qui est add
ed 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 dans Integer
objets, qui sont ajoutés à l' Set
. Cependant, gardez à l'esprit que les objets Integer
sont gérés en tant que Object
s, car il n'y a pas d'informations de type pour aider le compilateur à savoir quel type le Set
devrait attendre.
for (Object o : set) {
C'est la partie qui est cruciale. La raison pour laquelle la boucle for-each fonctionne est que Set
implémente le Iterable
interface, qui renvoie un Iterator
avec des informations de type, le cas échéant. (Iterator<T>
, c'est-à-dire.)
Cependant, comme il n'y a pas d'informations de type, le Set
retournera un Iterator
qui retournera les valeurs dans le Set
comme Object
s, et c'est pourquoi l'élément récupéré dans la boucle for-each doit être de type Object
.
Maintenant que le Object
est extrait de la Set
, il doit être converti dans un Integer
manuellement pour effectuer l'addition:
total += (Integer)o;
Ici, une typecast est effectuée d'un Object
à un Integer
. Dans ce cas, nous savons cela fonctionnera toujours, mais la typecasting manuelle me fait toujours sentir que c'est un code fragile qui pourrait être endommagé si un changement mineur est fait ailleurs. (Je sens que chaque typecast est un ClassCastException
qui attend de se produire, mais je m'égare...)
Le {[16] } est maintenant déballé dans un int
et autorisé à effectuer l'addition dans la variable int
total
.
J'espère pouvoir illustrer que les nouvelles fonctionnalités de Java 5 sont possibles avec du code non générique, mais ce n'est tout simplement pas aussi propre et straight-forward comme l'écriture de code avec des génériques. Et, à mon avis, pour profiter pleinement des nouvelles fonctionnalités de Java 5, il faut regarder dans les génériques, si à tout le moins, permet des vérifications à la compilation pour empêcher les Typecast invalides de lancer des exceptions à l'exécution.
Si vous deviez rechercher la base de données de bogues Java juste avant la sortie de 1.5, vous trouveriez sept fois plus de bogues avec NullPointerException
que ClassCastException
. Il ne semble donc pas que ce soit une excellente fonctionnalité pour trouver des bugs, ou du moins des bugs qui persistent après un petit test de fumée.
Pour moi, l'énorme avantage des génériques est qu'ils documentent dans le code des informations de type importantes. Si Je ne voulais pas que ces informations de type soient documentées dans le code, j'utiliserais un langage typé dynamiquement, ou au moins un langage avec une inférence de type plus implicite.
Garder les collections d'un objet à lui-même n'est pas un mauvais style (mais le style commun est d'ignorer efficacement l'encapsulation). Cela dépend plutôt de ce que vous faites. Passer des collections à des "algorithmes" est légèrement plus facile à vérifier (au moment de la compilation ou avant) avec des génériques.
Les génériques en Java facilitent le polymorphisme paramétrique . Au moyen de paramètres de type, vous pouvez passer des arguments aux types. Tout comme une méthode comme String foo(String s)
certains modèles de comportement, et 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>
certains modèles de comportement, et pas seulement pour un type spécifique, mais , pour tout type. List<T>
dit que , pour tout type T
, il existe un type de List
dont les éléments sont T
s. Donc List
est en fait un type constructeur. Il prend un type comme argument et construit un autre type en conséquence.
Voici quelques exemples de types génériques que j'utilise tous les jours. Tout d'abord, une interface générique très utile:
public interface F<A, B> {
public B f(A a);
}
Cette interface dit que pour les deux types, A
et B
, il existe une fonction (appelée f
) qui prend un A
et retourne un B
. lorsque vous implémentez cette interface, A
et B
peuvent être tous les types que vous voulez, tant que vous fournissez une fonction {[13] } qui prend le premier et renvoie le second. 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 les génériques, le polymorphisme était atteint par sous-classant en utilisant le mot clé extends
. Avec les génériques, nous pouvons réellement supprimer le sous-classement et utiliser le polymorphisme paramétrique à la place. Par exemple, considérons une classe paramétrée (générique) utilisée pour calculer les codes de hachage pour n'importe quel type. La place primordiale à l'Objet.hashCode (), nous utiliserions une classe générique comme ce:
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 flexible que d'utiliser l'héritage, car nous pouvons rester avec le thème de l'utilisation de la composition et du polymorphisme paramétrique sans verrouiller les hiérarchies fragiles.
Les génériques de Java ne sont pas parfaits cependant. Vous pouvez abstraire sur les types, mais vous ne pouvez pas abstraire sur les constructeurs de types, 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 A".
J'ai écrit un article sur ces limites des génériques Java, ici.
Une énorme victoire avec les génériques est qu'ils vous permettent d'éviter le sous-classement. Le sous-classement a tendance à entraîner des hiérarchies de classes fragiles qui sont difficiles à étendre, et des classes qui sont difficiles à comprendre individuellement sans regarder la hiérarchie entière.
Étaient avant les génériques, vous pourriez avoir des classes comme Widget
étendues par FooWidget
, BarWidget
, et BazWidget
, avec les génériques, vous pouvez avoir une seule classe générique {[24] } qui prend un Foo
, Bar
ou Baz
dans son constructeur pour vous donner Widget<Foo>
, Widget<Bar>
, et Widget<Baz>
.
Les génériques évitent le coup de performance de la boxe et du déballage. Fondamentalement, regardez ArrayList vs List
Je les aime juste parce qu'ils vous donnent un moyen rapide de définir un type personnalisé (comme je les utilise de toute façon).
Ainsi, par exemple, au lieu de définir une structure composée d'une chaîne et d'un entier, puis d'avoir à implémenter tout un ensemble d'objets et de méthodes pour accéder à un tableau de ces structures et ainsi de suite, vous pouvez simplement créer un dictionnaire
Dictionary<int, string> dictionary = new Dictionary<int, string>();
Et le compilateur / IDE fait le reste du levage lourd. Un Dictionnaire en particulier vous permet d'utiliser le premier type de clé (pas de valeurs répétées).
Le meilleur avantage pour les génériques est la réutilisation du code. Disons que vous avez beaucoup d'objets métier, et vous allez écrire du code très similaire pour chaque entité pour effectuer les mêmes actions. (C'est-à-dire les opérations Linq vers SQL).
Avec les génériques, vous pouvez créer une classe qui pourra fonctionner compte tenu de l'un des types qui héritent d'une classe de base donnée ou implémenter une interface donnée comme ceci:
public interface IEntity
{
}
public class Employee : IEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeID { get; set; }
}
public class Company : IEntity
{
public string Name { get; set; }
public string TaxID { get; set }
}
public class DataService<ENTITY, DATACONTEXT>
where ENTITY : class, IEntity, new()
where DATACONTEXT : DataContext, new()
{
public void Create(List<ENTITY> entities)
{
using (DATACONTEXT db = new DATACONTEXT())
{
Table<ENTITY> table = db.GetTable<ENTITY>();
foreach (ENTITY entity in entities)
table.InsertOnSubmit (entity);
db.SubmitChanges();
}
}
}
public class MyTest
{
public void DoSomething()
{
var dataService = new DataService<Employee, MyDataContext>();
dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
var otherDataService = new DataService<Company, MyDataContext>();
otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });
}
}
Notez la réutilisation du même service compte tenu des différents Types de Méthode DoSomething ci-dessus. Vraiment élégant!
, Il ya beaucoup d'autres bonnes raisons d'utiliser des génériques pour votre travail, c'est mon préféré.
Collections typées-même si vous ne voulez pas les utiliser, vous devrez probablement les traiter à partir d'autres bibliothèques , d'autres sources.
-
Typage Générique dans la création de classe:
Classe publique Foo { public t get ()...
-
Éviter le casting-j'ai toujours détesté des choses comme
Nouveau Comparateur { public int compareTo (objet o){ si (o instanceof classIcareAbout)...
Où vous êtes essentiellement vérification d'une condition qui ne devrait exister que parce que l'interface est exprimé en termes d'objets.
Ma réaction initiale aux génériques était similaire à la vôtre - "trop salissant, trop compliqué". Mon expérience est qu'après les avoir utilisés un peu, vous vous habituez à eux, et le code sans eux se sent moins clairement spécifié, et juste moins confortable. En dehors de cela, le reste du monde java les utilise, donc vous devrez éventuellement utiliser le programme, n'est-ce pas?
Pour donner un bon exemple. Imaginez que vous avez une classe appelée Foo
public class Foo
{
public string Bar() { return "Bar"; }
}
Exemple 1 Maintenant, vous voulez avoir une collection D'objets Foo. Vous avez deux options, LIst ou ArrayList, qui fonctionnent toutes deux de la même manière.
Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();
//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());
Dans le code ci-dessus, si j'essaie d'ajouter une classe de FireTruck au lieu de Foo, L'ArrayList l'ajoutera, mais la liste générique de Foo provoquera une exception.
Exemple deux.
Maintenant, vous avez vos deux listes de tableaux et vous voulez appeler la fonction Bar () sur chacun. Puisque Hte ArrayList est rempli d'objets, vous devez les lancer avant de pouvoir appeler bar. Mais puisque la liste générique de Foo ne peut contenir que des Foos, vous pouvez appeler Bar () directement sur ceux-ci.
foreach(object o in al)
{
Foo f = (Foo)o;
f.Bar();
}
foreach(Foo f in fl)
{
f.Bar();
}
N'avez-vous jamais écrit une méthode (ou une classe) où le concept clé de la méthode/classe n'était pas étroitement lié à un type de données spécifique des paramètres/variables d'instance (pensez liste liée, fonctions max/min, recherche binaire, etc.).
N'avez-vous jamais souhaité pouvoir réutiliser l'algorthm/code sans recourir à la réutilisation cut-n-paste ou compromettre le typage fort (par exemple, je veux un List
de Chaînes, pas un List
de choses que je espère sont des chaînes!)?
C'est pourquoi vous devriez voulez pour utiliser des génériques (ou quelque chose de mieux).
Le principal avantage, comme le souligne Mitchel, est le typage fort sans avoir besoin de définir plusieurs classes.
De cette façon, vous pouvez faire des choses comme:
List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();
Sans génériques, vous devrez convertir blah [0] au type correct pour accéder à ses fonctions.
Je sais que c'est une question C#, mais generics sont également utilisés dans d'autres langues, et leur utilisation/objectifs sont assez similaires.
Les collections Java utilisent generics depuis Java 1.5. Donc, un bon endroit pour les utiliser est lorsque vous créez votre propre objet de type collection.
Un exemple que je vois presque partout est une classe Pair, qui contient deux objets, mais doit traiter ces objets de manière générique.
class Pair<F, S> {
public final F first;
public final S second;
public Pair(F f, S s)
{
first = f;
second = s;
}
}
Chaque fois que vous utilisez cette classe de paire, vous pouvez spécifier quel type d'objets vous voulez qu'il traite et tous les problèmes de distribution de type apparaîtront au moment de la compilation, plutôt que lors de l'exécution.
Les génériques peuvent également avoir leurs limites définies avec les mots-clés 'super' et 'extends'. Par exemple, si vous voulez traiter un type générique mais que vous voulez vous assurer qu'il étend une classe appelée Foo (qui a une méthode setTitle):
public class FooManager <F extends Foo>{
public void setTitle(F foo, String title) {
foo.setTitle(title);
}
}
Bien que pas très intéressant en soi, il est utile de savoir que chaque fois que vous traitez avec un FooManager, vous savez qu'il va gérer les types MyClass, et que MyClass étend Foo.
De la documentation Sun Java, en réponse à " Pourquoi devrais-je utiliser des génériques?":
"Generics fournit un moyen pour vous de communiquer le type d'une collection au compilateur, afin qu'il puisse être vérifié. Une fois que le compilateur connaît le type d'élément de la collection, le compilateur peut vérifier que vous avez utilisé la collection de manière cohérente et pouvez insérer la bonne jette sur les valeurs de la collection... Le code utilisant des génériques est plus clair et plus sûr.... le compilateur peut vérifiez au moment de la compilation que les contraintes de type ne sont pas violées au moment de l'exécution [emphasis mine]. Parce que le programme compile sans avertissements, nous pouvons affirmer avec certitude qu'il ne lancera pas une ClassCastException au moment de l'exécution. L'effet net de l'utilisation de génériques, en particulier dans les grands programmes, est amélioration de la lisibilité et de la robustesse. [soulignement du mien] "
N'oubliez pas que les génériques ne sont pas seulement utilisés par des classes, ils peuvent également être utilisés par des méthodes. Par exemple, prenez l'extrait suivant:
private <T extends Throwable> T logAndReturn(T t) {
logThrowable(t); // some logging method that takes a Throwable
return t;
}
C'est simple, mais peut être utilisé très élégamment. La bonne chose est que la méthode retourne tout ce qu'elle a été donnée. Cela aide lorsque vous gérez des exceptions qui doivent être renvoyées à l'appelant:
...
} catch (MyException e) {
throw logAndReturn(e);
}
Le fait est que vous ne perdez pas le type en le passant par une méthode. Vous pouvez lancer le bon type d'exception au lieu de juste un Throwable
, ce qui serait tout ce que vous pourriez faire sans génériques.
Ceci est juste un exemple simple d'une utilisation pour les méthodes génériques. Il y a quelques autres choses intéressantes que vous pouvez faire avec des méthodes génériques. Le plus cool, à mon avis, est le type inférant avec des génériques. Prenez l'exemple suivant (tiré de L'édition Java 2nd de Josh Bloch):
...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
return new HashMap<K, V>();
}
Cela ne fait pas beaucoup, mais cela réduit l'encombrement lorsque les types génériques sont longs (ou imbriquées; c'est à dire Map<String, List<String>>
).
Les génériques vous permettent de créer des objets fortement typés, mais vous n'avez pas besoin de définir le type spécifique. Je pense que le meilleur exemple utile est la liste et les classes similaires.
En utilisant la liste générique, vous pouvez avoir une liste de Liste Liste ce que vous voulez et vous pouvez toujours référencer le typage fort, vous n'avez pas à convertir ou quelque chose comme vous le feriez avec un tableau ou une liste standard.
Les génériques vous permettent d'utiliser un typage fort pour les objets et les structures de données qui devraient pouvoir contenir n'importe quel objet. Il élimine également les typecasts fastidieux et coûteux lors de la récupération d'objets à partir de structures génériques (boxing/unboxing).
Un exemple qui utilise les deux est une liste liée. À quoi servirait une classe de liste liée si elle ne pouvait utiliser que object Foo? Pour implémenter une liste liée qui peut gérer n'importe quel type d'objet, la liste liée et les nœuds dans une classe interne de nœud hypothétique doivent être générique si vous voulez que la liste ne contienne qu'un seul type d'objet.
Si votre collection contient des types de valeur, ils n'ont pas besoin de box / unbox aux objets lorsqu'ils sont insérés dans la collection afin que vos performances augmentent considérablement. Cool add-ons comme resharper peut générer plus de code pour vous, comme foreach boucles.
Un autre avantage de L'utilisation de génériques (en particulier avec les Collections/listes) est que vous obtenez une vérification de type de compilation. Ceci est vraiment utile lorsque vous utilisez une liste générique au lieu d'une liste d'objets.
La raison la plus simple est qu'ils fournissent type de sécurité
List<Customer> custCollection = new List<Customer>;
Par opposition à,
object[] custCollection = new object[] { cust1, cust2 };
Comme un exemple simple.
En résumé, les génériques vous permettent de spécifier plus précisément ce que vous avez l'intention de faire (typage plus fort).
Cela a plusieurs avantages pour vous:
Parce que le compilateur sait plus sur ce que vous voulez faire, il vous permet d'omettre beaucoup de conversion de type, car il sait déjà que le type sera compatible.
Cela vous obtient également des commentaires plus tôt sur les correctnes de votre programme. Les choses qui auraient précédemment échoué à l'exécution (par exemple parce qu'un l'objet ne peut pas être converti dans le type désiré), échoue maintenant au moment de la compilation et vous pouvez corriger l'erreur avant que votre service de test ne dépose un rapport de bogue cryptique.
Le compilateur peut faire plus d'optimisations, comme éviter de boxe, etc.
Quelques choses à ajouter/développer (en parlant du point de vue. net):
Les types génériques vous permettent de créer des classes et des interfaces basées sur les rôles. Cela a déjà été dit en termes plus basiques, mais je trouve que vous commencez à concevoir votre code avec des classes qui sont implémentées de manière agnostique-ce qui entraîne un code hautement réutilisable.
Les arguments génériques sur les méthodes peuvent faire la même chose, mais ils aident aussi à appliquer le principe" Tell Don't Ask "au casting, c'est-à-dire" give me ce que je veux, et si vous ne pouvez pas, vous me dites pourquoi".
Je les utilise par exemple dans un GenericDao implémenté avec SpringORM et Hibernate qui ressemblent à ceci
public abstract class GenericDaoHibernateImpl<T>
extends HibernateDaoSupport {
private Class<T> type;
public GenericDaoHibernateImpl(Class<T> clazz) {
type = clazz;
}
public void update(T object) {
getHibernateTemplate().update(object);
}
@SuppressWarnings("unchecked")
public Integer count() {
return ((Integer) getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session) {
// Code in Hibernate for getting the count
}
}));
}
.
.
.
}
En utilisant des génériques, mes implémentations de ces Dao forcent le développeur à leur transmettre uniquement les entités pour lesquelles ils sont conçus en sous-classant simplement le GenericDao
public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
public UserDaoHibernateImpl() {
super(User.class); // This is for giving Hibernate a .class
// work with, as generics disappear at runtime
}
// Entity specific methods here
}
Mon petit framework est plus robuste (avoir des choses comme le filtrage, le chargement paresseux, la recherche). Je viens de simplifier ici pour vous donner un exemple
Je, comme Steve et vous, dit à la début "Trop salissant et compliqué", mais maintenant je vois ses avantages
Des avantages évidents tels que "type safety" et " no casting "sont déjà mentionnés, alors peut-être que je peux parler d'autres" avantages " que j'espère aider.
Tout d'abord , generics est un concept indépendant du langage et, IMO, cela pourrait avoir plus de sens si vous pensez au polymorphisme régulier (runtime) en même temps.
Par exemple, le polymorphisme que nous connaissons de la conception orientée objet a une notion d'exécution dans laquelle l'objet appelant est compris à l'exécution en tant que programme l'exécution va et la méthode pertinente est appelée en conséquence en fonction du type d'exécution. Dans les génériques, l'idée est quelque peu similaire mais tout se passe au moment de la compilation. Ce que cela signifie et comment vous l'utiliser?
(restons avec des méthodes génériques pour le garder compact) cela signifie que vous pouvez toujours avoir la même méthode sur des classes séparées (comme vous l'avez fait précédemment dans les classes polymorphes) mais cette fois elles sont générées automatiquement par le compilateur dépendent des types définis à la compilation temps. Vous paramétrez vos méthodes sur le type que vous donnez au moment de la compilation. Donc, au lieu d'écrire les méthodes à partir de zéro pour chaque type que vous avez comme vous le faites dans le polymorphisme d'exécution (substitution de méthode), vous laissez les compilateurs faire le travail pendant la compilation. Cela a un avantage évident puisque vous n'avez pas besoin de déduire tous les types possibles qui pourraient être utilisés dans votre système, ce qui le rend beaucoup plus évolutif sans changement de code.
Les Classes fonctionnent à peu près de la même manière. Vous paramétrer le type et le code est généré par le compilateur.
Une fois que vous avez l'idée de "temps de compilation", vous pouvez utiliser des types" délimités " et restreindre ce qui peut être passé en tant que type paramétré via des classes/méthodes. Ainsi, vous pouvez contrôler ce qu'il faut passer à travers ce qui est une chose puissante en particulier que vous avez un cadre consommé par d'autres personnes.
public interface Foo<T extends MyObject> extends Hoo<T>{
...
}
Personne ne peut définir sth autre que MyObject maintenant.
En outre, vous pouvez "appliquer" des contraintes de type sur votre méthode arguments ce qui signifie que vous pouvez vous assurer que les deux arguments de votre méthode dépendent du même type.
public <T extends MyObject> foo(T t1, T t2){
...
}
J'espère que tout cela a du sens.
Une fois, j'ai donné une conférence sur ce sujet. Vous pouvez trouver mes diapositives, le code et l'enregistrement audio à http://www.adventuresinsoftware.com/generics/.
L'utilisation de génériques pour les collections est simple et propre. Même si vous punt sur elle partout ailleurs, le gain des collections est une victoire pour moi.
List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
stuff.do();
}
Vs
List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
Stuff stuff = (Stuff)i.next();
stuff.do();
}
Ou
List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
Stuff stuff = (Stuff)stuffList.get(i);
stuff.do();
}
Cela seul vaut le "coût" marginal des génériques, et vous n'avez pas besoin d'être un gourou générique pour l'utiliser et obtenir de la valeur.
Les génériques vous donnent également la possibilité de créer des objets/méthodes plus réutilisables tout en fournissant un support spécifique au type. Vous gagnez également beaucoup de performance dans certains cas. Je ne connais pas la spécification complète sur les génériques Java, mais dans. net je peux spécifier des contraintes sur le paramètre Type, comme implémente une Interface , un constructeur et une dérivation.