Qu’est-ce qui est cool sur les génériques, pourquoi les utiliser?

Je pensais offrir ce softball à quiconque voudrait le sortir du parc. Quels sont les génériques, quels sont les avantages des génériques, pourquoi, où, comment les utiliser? S’il vous plaît, restz assez basique. Merci.

  • Vous permet d’écrire des méthodes de code / use library qui sont de type sécurisé, c.-à-d. Une liste est garantie pour être une liste de chaînes.
  • En raison de l’utilisation de génériques, le compilateur peut effectuer des vérifications à la compilation du code pour la sécurité du type, c’est-à-dire essayez-vous de placer un int dans cette liste de chaînes? L’utilisation d’un ArrayList entraînerait une erreur d’exécution moins transparente.
  • Plus rapide que d’utiliser des objects car cela évite la boxing / unboxing (où .net doit convertir les types de valeur en types de référence ou vice-versa ) ou la conversion d’objects vers le type de référence requirejs.
  • Vous permet d’écrire du code applicable à de nombreux types ayant le même comportement sous-jacent, c.-à-d. Un Dictionary utilise le même code sous-jacent qu’un Dictionary ; En utilisant des génériques, l’équipe du framework n’a dû écrire qu’un seul morceau de code pour obtenir les deux résultats avec les avantages susmentionnés.

Je déteste vraiment me répéter. Je déteste taper la même chose plus souvent que je dois. Je n’aime pas reformuler 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 brute pour une raison quelconque)

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

Je suis maintenant 3 fois plus efficace et je n’ai plus qu’à conserver une copie. Pourquoi ne voulez-vous PAS conserver moins de code?

Cela est également vrai pour les classes autres que les classes telles que Callable ou Reference qui doit interagir avec d’autres classes. Voulez-vous vraiment étendre Callable et Future et toutes les autres classes associées pour créer des versions sécurisées?

Je ne.

Le fait de ne pas avoir à transtyper est l’un des plus gros avantages des génériques Java , car il effectuera une vérification de type à la compilation. Cela réduira la possibilité de ClassCastException s qui peut être levée à l’exécution et peut conduire à un code plus robuste.

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

Chaque fois que je regarde les génériques, cela me donne mal à la tête. Je trouve que la meilleure partie de Java est la simplicité et la syntaxe minimale et les génériques ne sont pas simples et ajoutent une quantité importante de nouvelle syntaxe.

Au début, je ne voyais pas non plus les avantages 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 que je ne comprenais vraiment pas les avantages.

Les IDE modernes facilitent l’écriture de code avec les génériques.

Les IDE les plus modernes et les plus décents sont assez intelligents pour aider à écrire du code avec des génériques, en particulier avec l’achèvement du code.

Voici un exemple de création d’une Map avec une HashMap . Le code que je devrais taper est:

 Map m = new HashMap(); 

Et en effet, c’est beaucoup à taper juste pour créer un nouveau HashMap . Cependant, en réalité, je n’avais à taper que beaucoup avant qu’Eclipse ne sache ce dont j’avais besoin:

Map m = new Ha Ctrl + Espace

Il est vrai que je devais sélectionner HashMap dans une liste de candidats, mais en gros l’IDE savait quoi append, y compris les types génériques. Avec les bons outils, utiliser des génériques n’est pas trop mal.

De plus, comme les types sont connus, lors de la récupération d’éléments de la collection générique, l’EDI agira comme si cet object était déjà un object de son type déclaré – il n’est pas nécessaire de lancer un IDE pour savoir quel type d’object est.

Un avantage clé des génériques provient de la manière dont il fonctionne bien avec les nouvelles fonctionnalités de Java 5. Voici un exemple de lancement d’entiers dans un Set et de calcul de son total:

 Set set = new HashSet(); set.add(10); set.add(42); int total = 0; for (int i : set) { total += i; } 

Dans ce morceau de code, il existe trois nouvelles fonctionnalités Java 5:

  • Génériques
  • Autoboxing et unboxing
  • Pour chaque boucle

Premièrement, les génériques et la sélection automatique des primitives permettent les lignes suivantes:

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

L’entier 10 est automatiquement entré dans un Integer avec la valeur 10 . (Et même pour 42 ). Ensuite, cet Integer est jeté dans l’ Set connu pour contenir Integer s. Essayer de lancer une Ssortingng provoquerait une erreur de compilation.

Ensuite, pour chaque boucle, les trois sont les suivantes:

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

Tout d’abord, l’ Set contenant les Integer s est utilisé dans une boucle for-each. Chaque élément est déclaré être un int et est autorisé car le Integer est de retour à la primitive int . Et le fait que ce déballage se fasse est connu parce que les génériques ont été utilisés pour spécifier qu’il y avait des Integer dans l’ Set .

Les génériques peuvent être le ciment qui réunit les nouvelles fonctionnalités introduites dans Java 5, et rend le codage plus simple et plus sûr. Et la plupart du temps, les IDE sont suffisamment intelligents pour vous aider à faire de bonnes suggestions. En général, cela ne vous demandera pas beaucoup plus de frappe.

Et franchement, comme le montre l’exemple de Set , je pense que l’utilisation des fonctionnalités de Java 5 peut rendre le code plus concis et robuste.

Edit – Un exemple sans génériques

Ce qui suit est une illustration de l’exemple 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 vérifié au moment de la compilation.)

Lors de l’utilisation de collections non génériques, les types entrés dans la collection sont des objects de type Object . Par conséquent, dans cet exemple, un Object est ce qui est add dans l’ensemble.

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

Dans les lignes ci-dessus, la sélection automatique est en jeu – les valeurs primitives int 10 et 42 sont activées automatiquement dans des objects Integer , qui sont ajoutés à l’ Set . Cependant, gardez à l’esprit que les objects Integer sont gérés en tant qu’objects, car il n’y a pas d’informations de type pour aider le compilateur à savoir quel type l’ Set doit attendre.

 for (Object o : set) { 

C’est la partie qui est cruciale. La raison pour laquelle la boucle for-each fonctionne est que le Set implémente l’interface Iterable , qui retourne un Iterator avec des informations de type, le cas échéant. ( Iterator , c’est-à-dire.)

Cependant, comme il n’y a pas d’informations de type, le Set retourne un Iterator qui renverra les valeurs de l’ Object Set as, et c’est pourquoi l’élément récupéré dans la boucle for-each doit être de type Object .

Maintenant que l’ Object est extrait de l’ Set , il doit être Integer manuellement en un Integer pour effectuer l’addition suivante:

  total += (Integer)o; 

Ici, un transtypage est effectué d’un Object à un Integer . Dans ce cas, nous soaps que cela fonctionnera toujours, mais le typage manuel me fait toujours penser qu’il s’agit d’un code fragile qui pourrait être endommagé si une modification mineure est apscope ailleurs. (Je pense que chaque transtypage est une ClassCastException attendre, mais je digresse …)

Le Integer est maintenant décoché dans un int et autorisé à effectuer l’addition dans le total variable int .

J’espère que je pourrais illustrer que les nouvelles fonctionnalités de Java 5 sont possibles avec du code non générique, mais ce n’est pas aussi simple et direct que d’écrire du code avec des génériques. Et, à mon avis, pour tirer pleinement parti des nouvelles fonctionnalités de Java 5, il faut se pencher sur les génériques, au minimum, autoriser les contrôles à la compilation pour empêcher les fautes de frappe non valides lors de l’exécution.

Si vous deviez rechercher la firebase database de bogues Java juste avant la ClassCastException de la ClassCastException 1.5, vous trouveriez sept fois plus de bogues avec NullPointerException que ClassCastException . Donc, il ne semble pas que ce soit une bonne fonctionnalité pour trouver des bugs, ou du moins des bugs qui persistent après un petit test de fumée.

Pour moi, le grand avantage des génériques est qu’ils documentent en code des informations de type importantes. Si je ne voulais pas que ces informations de type soient documentées dans le code, j’utiliserais alors un langage typé dynamicment, ou du moins un langage avec une inférence de type plus implicite.

Garder les collections d’un object sur lui-même n’est pas un mauvais style (mais le style commun consiste à 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 les génériques.

Les génériques en Java facilitent le polymorphism paramésortingque . Au moyen de parameters de type, vous pouvez transmettre des arguments aux types. Tout comme une méthode telle que Ssortingng foo(Ssortingng s) modélise un comportement, pas seulement pour une chaîne particulière, mais pour toute chaîne s , un type comme List modélise un comportement, pas seulement pour un type spécifique, mais pour tout type. type . List dit que pour tout type T , il existe un type de List dont les éléments sont T s . So List est en fait un constructeur de type . 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 { public B f(A a); } 

Cette interface dit que pour deux types, A et B , il y a une fonction (appelée f ) qui prend un A et renvoie un B Lorsque vous implémentez cette interface, A et B peuvent être tous les types souhaités, à condition que vous fournissiez une fonction f qui prenne la première et retourne la dernière. Voici un exemple d’implémentation de l’interface:

 F intToSsortingng = new F() { public Ssortingng f(int i) { return Ssortingng.valueOf(i); } } 

Avant les génériques, le polymorphism était obtenu en sous-classant en utilisant le mot extends clé extend. Avec les génériques, on peut en fait supprimer les sous-classes et utiliser à la place le polymorphism paramésortingque. Par exemple, considérons une classe paramétrée (générique) utilisée pour calculer des codes de hachage pour tout type. Au lieu de remplacer Object.hashCode (), nous utiliserions une classe générique comme celle-ci:

 public final class Hash { private final F hashFunction; public Hash(final F f) { this.hashFunction = f; } public int hash(A a) { return hashFunction.f(a); } } 

C’est beaucoup plus flexible que d’utiliser l’inheritance, car nous pouvons restr sur le thème de l’utilisation du polymorphism paramésortingque et de la composition sans bloquer les hiérarchies fragiles.

Les génériques de Java ne sont cependant pas parfaits. Vous pouvez faire un résumé sur des types, mais vous ne pouvez pas faire de résumé sur des constructeurs de types, par exemple. Autrement dit, 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.

Un grand avantage 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 difficiles à comprendre individuellement sans examiner toute la hiérarchie.

Avant génériques, vous pourriez avoir des classes comme Widget étendu par FooWidget , BarWidget et BazWidget , avec des génériques vous pouvez avoir une seule classe générique Widget qui prend un Foo , Bar ou Baz dans son constructeur pour vous donner Widget , Widget , et Widget .

Les génériques évitent les performances de boxe et de déballage. Fondamentalement, regardez ArrayList vs List . Les deux font les mêmes choses de base, mais List sera beaucoup plus rapide car vous n’avez pas besoin de cocher / à partir de l’object.

Je les aime juste car ils vous permettent de définir rapidement un type personnalisé (comme je les utilise quand même).

Ainsi, par exemple, au lieu de définir une structure composée d’une chaîne et d’un nombre entier, et de devoir implémenter tout un ensemble d’objects et de méthodes pour accéder à un tableau de ces structures, vous pouvez créer un dictionnaire.

 Dictionary dictionary = new Dictionary(); 

Et le compilateur / IDE fait le rest du travail lourd. Un dictionnaire en particulier vous permet d’utiliser le premier type comme 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’objects métier et que vous allez écrire un code TRÈS similaire pour chaque entité pour effectuer les mêmes actions. (Opérations IE Linq to SQL).

Avec les génériques, vous pouvez créer une classe capable de fonctionner avec n’importe quel type héritant d’une classe de base donnée ou implémentant une interface donnée, comme ceci:

 public interface IEntity { } public class Employee : IEntity { public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public int EmployeeID { get; set; } } public class Company : IEntity { public ssortingng Name { get; set; } public ssortingng TaxID { get; set } } public class DataService where ENTITY : class, IEntity, new() where DATACONTEXT : DataContext, new() { public void Create(List entities) { using (DATACONTEXT db = new DATACONTEXT()) { Table table = db.GetTable(); foreach (ENTITY entity in entities) table.InsertOnSubmit (entity); db.SubmitChanges(); } } } public class MyTest { public void DoSomething() { var dataService = new DataService(); dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 }); var otherDataService = new DataService(); otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" }); } } 

Notez la réutilisation du même service en fonction des différents types de la méthode DoSomething ci-dessus. Vraiment élégant!

Il y a 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 depuis 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é les choses comme

    nouveau Comparator {public int compareTo (Object o) {if (o instanceof classIcareAbout) …

Où vous recherchez essentiellement une condition qui ne devrait exister que parce que l’interface est exprimée en termes d’objects.

Ma réaction initiale aux génériques était similaire à la vôtre – “trop ​​désordonnée, trop compliquée”. Mon expérience est que, après les avoir utilisés un peu, vous vous y habituez, et le code sans eux se sent moins clairement spécifié, et simplement moins confortable. En dehors de cela, le rest du monde Java les utilise pour que vous deviez finir avec le programme, non?

Pour donner un bon exemple. Imaginez que vous ayez une classe appelée Foo

 public class Foo { public ssortingng Bar() { return "Bar"; } } 

Exemple 1 Maintenant, vous voulez avoir une collection d’objects Foo. Vous avez deux options, LIst ou ArrayList, toutes deux fonctionnant de manière similaire.

 Arraylist al = new ArrayList(); List fl = new List(); //code to add Foos al.Add(new Foo()); f1.Add(new Foo()); 

Dans le code ci-dessus, si j’essaie d’append une classe de FireTruck au lieu de Foo, le ArrayList l’appenda, mais la liste générique de Foo provoquera la levée d’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’objects, vous devez les lancer avant de pouvoir appeler bar. Mais comme 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 parameters / variables d’instance (liste liée, fonctions max / min, recherche binary , etc.).

N’avez-vous jamais souhaité pouvoir réutiliser l’algorthm / code sans recourir à la réutilisation de cut-n-paste ou compromettre le typage fort (par exemple, je veux une List de chaînes, pas une List de choses que j’espère être des chaînes!)?

C’est pourquoi vous devriez vouloir utiliser des génériques (ou quelque chose de mieux).

Comme le souligne Mitchel, le principal avantage est le fort typage sans avoir à définir plusieurs classes.

De cette façon, vous pouvez faire des choses comme:

 List blah = new List(); blah[0].SomeCustomFunction(); 

Sans les génériques, vous devrez convertir le blah [0] au bon type pour accéder à ses fonctions.

le jvm jette quand même … il crée implicitement du code qui traite le type générique comme “object” et crée des moulages à l’instanciation désirée. Les génériques Java ne sont que du sucre syntaxique.

Je sais que c’est une question C #, mais les génériques sont également utilisés dans d’autres langues, et leur utilisation / objectives sont assez similaires.

Les collections Java utilisent des génériques depuis Java 1.5. Donc, un bon endroit pour les utiliser est lorsque vous créez votre propre object de type collection.

Un exemple que je vois presque partout est une classe Pair, qui contient deux objects, mais doit traiter ces objects de manière générique.

 class Pair { 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 Pair, vous pouvez spécifier le type d’objects que vous voulez traiter et tous les problèmes de type cast seront affichés 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 «extend». Par exemple, si vous voulez gérer 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 { public void setTitle(F foo, Ssortingng title) { foo.setTitle(title); } } 

Bien que ce ne soit pas très intéressant en soi, il est utile de savoir que chaque fois que vous manipulez un FooManager, vous savez qu’il va gérer les types MyClass, et que MyClass étend Foo.

À partir de la documentation de Sun Java, en réponse à “pourquoi devrais-je utiliser des génériques?”:

“Les génériques vous permettent 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 les castings corrects sur les valeurs étant retirés de la collection … Le code utilisant des génériques est plus clair et plus sûr …. le compilateur peut vérifier au moment de la compilation que les contraintes de type ne sont pas violées au moment de l’exécution . programme comstack sans avertissements, nous pouvons affirmer avec certitude qu’il ne va pas lancer une exception ClassCastException au moment de l’exécution. L’utilisation de génériques, en particulier dans les grands programmes, a pour effet d’ améliorer la lisibilité et la robustesse .

N’oubliez pas que les génériques ne sont pas seulement utilisés par les classes, ils peuvent également être utilisés par les méthodes. Par exemple, prenez l’extrait suivant:

 private  T logAndReturn(T t) { logThrowable(t); // some logging method that takes a Throwable return t; } 

C’est simple, mais peut être utilisé avec beaucoup d’élégance. Ce qui est bien, c’est que la méthode retourne 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 type d’exception correct au lieu d’un object Throwable , ce qui serait tout ce que vous pourriez faire sans les génériques.

Ceci est juste un exemple simple d’utilisation d’une méthode générique. Il y a pas mal d’autres choses intéressantes à faire avec les méthodes génériques. Le plus cool, à mon avis, est de type inférant avec des génériques. Prenons l’exemple suivant (tiré de Effective Java 2nd Edition de Josh Bloch):

 ... Map myMap = createHashMap(); ... public  Map createHashMap() { return new HashMap(); } 

Cela ne fait pas grand chose, mais cela réduit l’encombrement lorsque les types génériques sont longs (ou nesteds, par exemple Map> ).

Generics allow you to create objects that are strongly typed, yet you don’t have to define the specific type. I think the best useful example is the List and similar classes.

Using the generic list you can have a List List List whatever you want and you can always reference the strong typing, you don’t have to convert or anything like you would with a Array or standard List.

Generics let you use strong typing for objects and data structures that should be able to hold any object. It also eliminates tedious and expensive typecasts when resortingeving objects from generic structures (boxing/unboxing).

One example that uses both is a linked list. What good would a linked list class be if it could only use object Foo? To implement a linked list that can handle any kind of object, the linked list and the nodes in a hypothetical node inner class must be generic if you want the list to contain only one type of object.

If your collection contains value types, they don’t need to box/unbox to objects when inserted into the collection so your performance increases dramatically. Cool add-ons like resharper can generate more code for you, like foreach loops.

Another advantage of using Generics (especially with Collections/Lists) is you get Comstack Time Type Checking. This is really useful when using a Generic List instead of a List of Objects.

Single most reason is they provide Type safety

 List custCollection = new List; 

as opposed to,

 object[] custCollection = new object[] { cust1, cust2 }; 

as a simple example.

In summary, generics allow you to specify more precisily what you intend to do (stronger typing).

This has several benefits for you:

  • Because the comstackr knows more about what you want to do, it allows you to omit a lot of type-casting because it already knows that the type will be compatible.

  • This also gets you earlier feedback about the correctnes of your program. Things that previously would have failed at runtime (eg because an object couldn’t be casted in the desired type), now fail at comstack-time and you can fix the mistake before your testing-department files a cryptical bug report.

  • The comstackr can do more optimizations, like avoiding boxing, etc.

A couple of things to add/expand on (speaking from the .NET point of view):

Generic types allow you to create role-based classes and interfaces. This has been said already in more basic terms, but I find you start to design your code with classes which are implemented in a type-agnostic way – which results in highly reusable code.

Generic arguments on methods can do the same thing, but they also help apply the “Tell Don’t Ask” principle to casting, ie “give me what I want, and if you can’t, you tell me why”.

I use them for example in a GenericDao implemented with SpringORM and Hibernate which look like this

 public abstract class GenericDaoHibernateImpl extends HibernateDaoSupport { private Class type; public GenericDaoHibernateImpl(Class 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 } })); } . . . } 

By using generics my implementations of this DAOs force the developer to pass them just the entities they are designed for by just subclassing the GenericDao

 public class UserDaoHibernateImpl extends GenericDaoHibernateImpl { public UserDaoHibernateImpl() { super(User.class); // This is for giving Hibernate a .class // work with, as generics disappear at runtime } // Entity specific methods here } 

My little framework is more robust (have things like filtering, lazy-loading, searching). I just simplified here to give you an example

I, like Steve and you, said at the beginning “Too messy and complicated” but now I see its advantages

Obvious benefits like “type safety” and “no casting” are already mentioned so maybe I can talk about some other “benefits” which I hope it helps.

First of all, generics is a language-independent concept and , IMO, it might make more sense if you think about regular (runtime) polymorphism at the same time.

For example, the polymorphism as we know from object oriented design has a runtime notion in where the caller object is figured out at runtime as program execution goes and the relevant method gets called accordingly depending on the runtime type. In generics, the idea is somewhat similar but everything happens at comstack time. What does that mean and how you make use of it?

(Let’s stick with generic methods to keep it compact) It means that you can still have the same method on separate classes (like you did previously in polymorphic classes) but this time they’re auto-generated by the comstackr depend on the types set at comstack time. You paramesortingse your methods on the type you give at comstack time. So, instead of writing the methods from scratch for every single type you have as you do in runtime polymorphism (method overriding), you let comstackrs do the work during compilation. This has an obvious advantage since you don’t need to infer all possible types that might be used in your system which makes it far more scalable without a code change.

Classes work the pretty much same way. You paramesortingse the type and the code is generated by the comstackr.

Once you get the idea of “comstack time”, you can make use “bounded” types and ressortingct what can be passed as a paramesortingsed type through classes/methods. So, you can control what to be passed through which is a powerful thing especially you’ve a framework being consumed by other people.

 public interface Foo extends Hoo{ ... } 

No one can set sth other than MyObject now.

Also, you can “enforce” type constraints on your method arguments which means you can make sure both your method arguments would depend on the same type.

 public  foo(T t1, T t2){ ... } 

Hope all of this makes sense.

I once gave a talk on this topic. You can find my slides, code, and audio recording at http://www.adventuresinsoftware.com/generics/ .

Using generics for collections is just simple and clean. Even if you punt on it everywhere else, the gain from the collections is a win to me.

 List stuffList = getStuff(); for(Stuff stuff : stuffList) { stuff.do(); } 

contre

 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(); } 

That alone is worth the marginal "cost" of generics, and you don't have to be a generic Guru to use this and get value.

Generics also give you the ability to create more reusable objects/methods while still providing type specific support. You also gain a lot of performance in some cases. I don’t know the full spec on the Java Generics, but in .NET I can specify constraints on the Type parameter, like Implements a Interface, Constructor , and Derivation.