Lo bueno de los generics, ¿por qué usarlos?

Pensé que ofrecería esta pelota de softball a quien quisiera golpearla fuera del parque. ¿Qué son los generics, cuáles son las ventajas de los generics, por qué, dónde, cómo debo usarlos? Por favor, mantenlo bastante básico. Gracias.

  • Le permite escribir código / usar métodos de biblioteca que son seguros para el tipo, es decir, se garantiza que List sea una lista de cadenas.
  • Como resultado del uso de generics, el comstackdor puede realizar comprobaciones en tiempo de comstackción del código de tipo seguridad, es decir, ¿está tratando de poner un int en esa lista de cadenas? El uso de ArrayList haría que fuera un error de tiempo de ejecución menos transparente.
  • Más rápido que usar objetos ya que evita el boxeo / unboxing (donde .net tiene que convertir tipos de valores a tipos de referencia o viceversa ) o el fundido desde objetos al tipo de referencia requerido.
  • Le permite escribir código que es aplicable a muchos tipos con el mismo comportamiento subyacente, es decir, un Diccionario usa el mismo código subyacente que un Diccionario ; usando generics, el equipo del marco solo tuvo que escribir una pieza de código para lograr ambos resultados con las ventajas antes mencionadas también.

Realmente odio repetirme. Odio escribir lo mismo más a menudo que lo que tengo que hacer. No me gusta repetir las cosas varias veces con pequeñas diferencias.

En lugar de crear:

class MyObjectList { MyObject get(int index) {...} } class MyOtherObjectList { MyOtherObject get(int index) {...} } class AnotherObjectList { AnotherObject get(int index) {...} } 

Puedo construir una clase reutilizable … (en el caso de que no quieras usar la colección sin procesar por algún motivo)

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

Ahora soy 3 veces más eficiente y solo tengo que mantener una copia. ¿Por qué NO querrías mantener menos código?

Esto también es cierto para las clases que no son de colección, como una Callable o una Reference que tiene que interactuar con otras clases. ¿Realmente desea extender Callable y Future y cualquier otra clase asociada para crear versiones de tipo seguro?

Yo no.

No necesitar el encasillado es una de las mayores ventajas de los generics de Java , ya que realizará la verificación de tipos en tiempo de comstackción. Esto reducirá la posibilidad de ClassCastException s que se puede lanzar en el tiempo de ejecución, y puede conducir a un código más robusto.

Pero sospecho que eres completamente consciente de eso.

Cada vez que miro a Generics me da dolor de cabeza. En mi opinión, la mejor parte de Java es su simplicidad y la syntax mínima y los generics no son simples y añaden una cantidad significativa de syntax nueva.

Al principio, tampoco vi el beneficio de los generics. Comencé a aprender Java a partir de la syntax 1.4 (a pesar de que Java 5 estaba fuera en ese momento) y cuando encontré generics, sentí que era más código para escribir, y realmente no entendía los beneficios.

Los IDEs modernos hacen que escribir código con generics sea más fácil.

La mayoría de los IDEs modernos y decentes son lo suficientemente inteligentes como para ayudar a escribir código con generics, especialmente con la finalización de código.

Aquí hay un ejemplo de hacer un Map con un HashMap . El código que tendré que escribir es:

 Map m = new HashMap(); 

Y de hecho, eso es mucho para escribir solo para hacer un nuevo HashMap . Sin embargo, en realidad, solo tuve que escribir esto mucho antes de que Eclipse supiera lo que necesitaba:

Map m = new Ha Ctrl + Espacio

Es cierto que tuve que seleccionar HashMap de una lista de candidatos, pero básicamente el IDE sabía qué agregar, incluidos los tipos generics. Con las herramientas adecuadas, usar generics no es tan malo.

Además, dado que los tipos son conocidos, cuando se recuperan elementos de la colección genérica, el IDE actuará como si ese objeto ya fuera un objeto de su tipo declarado; no hay necesidad de convertir el IDE para saber cuál es el tipo de objeto es.

Una ventaja clave de los generics proviene de la forma en que funciona bien con las nuevas características de Java 5. Aquí hay un ejemplo de lanzar enteros a un Set y calcular su total:

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

En esa pieza de código, hay tres nuevas características de Java 5 presentes:

  • Genéricos
  • Autoboxing y unboxing
  • Para cada bucle

Primero, los generics y el autoboxing de primitivas permiten las siguientes líneas:

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

El número entero 10 es autoboxed en un Integer con el valor de 10 . (Y lo mismo para 42 ). Luego ese Integer se arroja al Set que se sabe que contiene Integer . Intentar arrojar una String provocaría un error de comstackción.

A continuación, para for-each loop toma los tres de los siguientes:

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

Primero, el Set contiene Integer se usa en un bucle for-each. Cada elemento se declara como int y se permite a medida que el Integer se vuelve a empaquetar en la primitiva int . Y el hecho de que este desempaquetado ocurra es conocido porque los generics se usaron para especificar que había Integer retenidos en el Set .

Los generics pueden ser el pegamento que reúne las nuevas características introducidas en Java 5, y simplemente hace que la encoding sea más simple y segura. Y la mayoría de las veces, los IDEs son lo suficientemente inteligentes como para ayudarte con buenas sugerencias, por lo que, en general, no será mucho más tipeo.

Y francamente, como se puede ver en el ejemplo de Set , creo que la utilización de las características de Java 5 puede hacer que el código sea más conciso y robusto.

Editar – Un ejemplo sin generics

La siguiente es una ilustración del ejemplo de Set anterior sin el uso de generics. Es posible, pero no es exactamente agradable:

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

(Nota: el código anterior generará una advertencia de conversión no verificada en tiempo de comstackción).

Al usar colecciones no genéricas, los tipos que se ingresan en la colección son objetos de tipo Object . Por lo tanto, en este ejemplo, un Object es lo que se add al conjunto.

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

En las líneas anteriores, el autoboxing está en juego: el valor int primitivo 10 y 42 se están autocapturando en objetos Integer , que se están agregando al Set . Sin embargo, tenga en cuenta que los objetos Integer se manejan como Object , ya que no hay información de tipo para ayudar al comstackdor a saber qué tipo debe esperar el Set .

 for (Object o : set) { 

Esta es la parte que es crucial. El motivo por el que funciona el ciclo for-each es porque el Set implementa la interfaz Iterable , que devuelve un Iterator con información de tipo, si está presente. ( Iterator , eso es)

Sin embargo, dado que no hay información de tipo, el Set devolverá un Iterator que devolverá los valores en el Set como Object s, y es por eso que el elemento que se recupera en el bucle for-each debe ser de tipo Object .

Ahora que el Object se recupera del Set , se debe convertir a un Integer manualmente para realizar la sum:

  total += (Integer)o; 

Aquí, un tipo de transmisión se realiza desde un Object a un Integer . En este caso, sabemos que esto siempre funcionará, pero el encasillado manual siempre me hace sentir que es un código frágil que podría dañarse si se realiza un cambio menor en otro lugar. (Siento que cada tipocast es una ClassCastException esperando a suceder, pero estoy divagando …)

El Integer ahora está desagrupado en un int y se le permite realizar la sum en el total variable int .

Espero poder ilustrar que las nuevas características de Java 5 se pueden usar con código no genérico, pero simplemente no es tan limpio y sencillo como escribir código con generics. Y, en mi opinión, para aprovechar al máximo las nuevas características en Java 5, uno debería buscar en los generics, si al menos, permite verificaciones en tiempo de comstackción para evitar que los tipos de letra inválidos emitan excepciones en el tiempo de ejecución.

Si tuviera que buscar en la base de datos de errores de Java justo antes de que se lanzara 1.5, encontraría siete veces más errores con NullPointerException que ClassCastException . Entonces, no parece que sea una gran característica encontrar errores, o al menos errores que persisten después de un poco de prueba de humo.

Para mí, la gran ventaja de los generics es que documentan en código la información de tipo importante. Si no quisiera que ese tipo de información esté documentada en código, entonces utilizaría un lenguaje de tipado dynamic, o al menos un lenguaje con inferencia de tipo más implícita.

Mantener las colecciones de un objeto para sí mismo no es un mal estilo (pero el estilo común es ignorar eficazmente la encapsulación). Más bien depende de lo que estás haciendo. Pasar colecciones a “algoritmos” es un poco más fácil de verificar (en o antes del tiempo de comstackción) con generics.

Los generics en Java facilitan el polymorphism paramétrico . Por medio de parámetros de tipo, puede pasar argumentos a tipos. Así como un método como String foo(String s) modela algún comportamiento, no solo para una cadena en particular, sino para cualquier cadena, por lo que un tipo como List modela algún comportamiento, no solo para un tipo específico, sino para cualquier tipo . List dice que para cualquier tipo T , hay un tipo de List cuyos elementos son T s . Así que List es en realidad un constructor de tipos . Toma un tipo como argumento y construye otro tipo como resultado.

Aquí hay un par de ejemplos de tipos generics que uso todos los días. Primero, una interfaz genérica muy útil:

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

Esta interfaz dice que para algunos dos tipos, A y B , hay una función (llamada f ) que toma una A y devuelve una B Cuando implementa esta interfaz, A y B pueden ser de cualquier tipo que desee, siempre que proporcione una función f que tome la primera y devuelva la última. Aquí hay una implementación de ejemplo de la interfaz:

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

Antes de los generics, el polymorphism se lograba subclasando usando la palabra clave extends . Con los generics, en realidad podemos eliminar la subclasificación y usar polymorphism paramétrico. Por ejemplo, considere una clase parametrizada (genérica) utilizada para calcular códigos hash para cualquier tipo. En lugar de anular Object.hashCode (), usaríamos una clase genérica como esta:

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

Esto es mucho más flexible que usar herencia, porque podemos seguir con el tema del uso de la composición y el polymorphism paramétrico sin bloquear las frágiles jerarquías.

Sin embargo, los generics de Java no son perfectos. Puede abstraer sobre tipos, pero no puede abstraer sobre constructores de tipo, por ejemplo. Es decir, puede decir “para cualquier tipo T”, pero no puede decir “para ningún tipo T que tenga un parámetro de tipo A”.

Escribí un artículo sobre estos límites de los generics de Java, aquí.

Una gran victoria con los generics es que te permiten evitar las subclases. Las subclases tienden a dar como resultado jerarquías de clases frágiles que son difíciles de extender, y clases que son difíciles de entender individualmente sin mirar toda la jerarquía.

Wereas antes de generics puede tener clases como Widget extendido por FooWidget , BarWidget y BazWidget , con generics puede tener un Widget clase genérico Widget que toma un Foo , Bar o Baz en su constructor para darle Widget , Widget y Widget .

Los generics evitan el golpe de rendimiento del boxeo y el desempaquetado. Básicamente, mira ArrayList vs List . Ambos hacen las mismas cosas centrales, pero List será mucho más rápido porque no tiene que encajonar hacia / desde el objeto.

Simplemente me gustan porque te dan una forma rápida de definir un tipo personalizado (como los uso de todos modos).

Entonces, por ejemplo, en lugar de definir una estructura que consta de una cadena y un número entero, y luego tener que implementar un conjunto completo de objetos y métodos sobre cómo acceder a una matriz de esas estructuras, etc., puede hacer un diccionario

 Dictionary dictionary = new Dictionary(); 

Y el comstackdor / IDE hace el rest del trabajo pesado. Un diccionario en particular le permite usar el primer tipo como clave (sin valores repetidos).

El mejor beneficio para Generics es la reutilización de código. Digamos que tiene muchos objetos comerciales, y va a escribir un código MUY similar para cada entidad para realizar las mismas acciones. (IE Linq a operaciones SQL).

Con generics, puede crear una clase que pueda funcionar dado cualquiera de los tipos heredados de una clase base dada o implementar una interfaz determinada como esta:

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

Observe la reutilización del mismo servicio dados los diferentes tipos en el método DoSomething anterior. Verdaderamente elegante!

Hay muchas otras buenas razones para usar medicamentos generics para su trabajo, este es mi favorito.

  • Colecciones tipificadas: incluso si no desea utilizarlas, es probable que tenga que tratarlas desde otras bibliotecas, otras fonts.

  • Tipeo genérico en la creación de clases:

    clase pública Foo {public T get () …

  • Evitar el casting: siempre me han desagradado cosas como

    nuevo comparador {public int compareTo (Object o) {if (o instanceof classIcareAbout) …

Donde esencialmente está buscando una condición que solo debería existir porque la interfaz se expresa en términos de objetos.

Mi reacción inicial a los generics fue similar a la tuya: “demasiado desordenada, demasiado complicada”. Mi experiencia es que después de usarlos por un tiempo, te acostumbras a ellos, y el código sin ellos se siente menos claramente especificado, y es menos cómodo. Aparte de eso, el rest del mundo java los usa así que vas a tener que seguir con el progtwig eventualmente, ¿verdad?

Para dar un buen ejemplo. Imagina que tienes una clase llamada Foo

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

Ejemplo 1 Ahora quiere tener una colección de objetos Foo. Tienes dos opciones, LIst o ArrayList, que funcionan de manera similar.

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

En el código anterior, si trato de agregar una clase de FireTruck en lugar de Foo, ArrayList lo agregará, pero la Lista genérica de Foo hará que se genere una excepción.

Ejemplo dos

Ahora tiene sus dos listas de matriz y desea llamar a la función Bar () en cada una. Como hte ArrayList está lleno de objetos, debe lanzarlos antes de poder llamar a la barra. Pero dado que la Lista genérica de Foo solo puede contener Foos, puede llamar a Bar () directamente en esos.

 foreach(object o in al) { Foo f = (Foo)o; f.Bar(); } foreach(Foo f in fl) { f.Bar(); } 

¿Alguna vez ha escrito un método (o una clase) donde el concepto clave del método / clase no estaba estrechamente ligado a un tipo de datos específico de los parámetros / variables de instancia (lista de enlaces pensados, funciones máxima / mínima, búsqueda binaria , etc.)

¿Alguna vez has deseado poder reutilizar el algorthm / código sin recurrir a la reutilización de cortar y pegar o a comprometer la tipificación fuerte (por ejemplo, quiero una List de cadenas, no una List de cosas que espero sean cadenas)?

Es por eso que deberías querer usar generics (o algo mejor).

La ventaja principal, como lo señala Mitchel, es el tipado fuerte sin la necesidad de definir múltiples clases.

De esta forma puedes hacer cosas como estas:

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

Sin generics, debería lanzar blah [0] al tipo correcto para acceder a sus funciones.

el jvm arroja de todos modos … crea código de forma implícita que trata el tipo genérico como “Objeto” y crea moldes para la instanciación deseada. Los generics de Java son solo azúcar sintáctico.

Sé que esta es una pregunta de C #, pero los generics también se usan en otros idiomas, y su uso / objectives son bastante similares.

Las colecciones de Java usan generics desde Java 1.5. Entonces, un buen lugar para usarlos es cuando está creando su propio objeto similar a una colección.

Un ejemplo que veo casi en todas partes es una clase Pair, que contiene dos objetos, pero necesita tratar esos objetos de una manera genérica.

 class Pair { public final F first; public final S second; public Pair(F f, S s) { first = f; second = s; } } 

Siempre que use esta clase Pair, puede especificar qué tipo de objetos desea que trate y cualquier tipo de problema de conversión aparecerá en tiempo de comstackción, en lugar de en tiempo de ejecución.

Los generics también pueden tener sus límites definidos con las palabras clave ‘super’ y ‘extends’. Por ejemplo, si quiere tratar con un tipo genérico pero quiere asegurarse de que amplíe una clase llamada Foo (que tiene un método setTitle):

 public class FooManager { public void setTitle(F foo, String title) { foo.setTitle(title); } } 

Si bien no es muy interesante por sí mismo, es útil saber que cada vez que maneje un FooManager, sabrá que manejará los tipos de MyClass, y que MyClass se extenderá a Foo.

De la documentación de Sun Java, en respuesta a “¿por qué debería usar generics?”:

“Generics proporciona una forma de comunicar el tipo de una colección al comstackdor, para que pueda verificarse. Una vez que el comstackdor conoce el tipo de elemento de la colección, el comstackdor puede verificar que ha utilizado la colección de forma coherente y puede insertar los lanzamientos correctos sobre los valores que se sacan de la colección … El código que usa generics es más claro y seguro … el comstackdor puede verificar en tiempo de comstackción que las restricciones de tipo no se violan en el tiempo de ejecución [énfasis mío]. progtwig comstack sin advertencias, podemos afirmar con certeza que no arrojará una ClassCastException en tiempo de ejecución. El efecto neto del uso de generics, especialmente en progtwigs grandes, es la legibilidad y legibilidad mejoradas . [énfasis mío] ”

No olvide que los generics no solo son utilizados por las clases, sino que también pueden usarse por métodos. Por ejemplo, tome el siguiente fragmento:

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

Es simple, pero se puede usar muy elegantemente. Lo bueno es que el método devuelve lo que fue que se le dio. Esto ayuda cuando se manejan excepciones que necesitan ser devueltas a la persona que llama:

  ... } catch (MyException e) { throw logAndReturn(e); } 

El punto es que no pierdes el tipo pasándolo por un método. Puede lanzar el tipo correcto de excepción en lugar de solo un Throwable , que sería todo lo que podría hacer sin los generics.

Este es solo un simple ejemplo de un uso para métodos generics. Hay muchas otras cosas buenas que puedes hacer con métodos generics. Lo más genial, en mi opinión, es el tipo de inferir con generics. Tome el siguiente ejemplo (tomado de Effective Java 2nd Edition de Josh Bloch):

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

Esto no hace mucho, pero reduce el desorden cuando los tipos generics son largos (o nesteds, es decir, Map> ).

Los generics te permiten crear objetos fuertemente tipados, pero no tienes que definir el tipo específico. Creo que el mejor ejemplo útil es la Lista y clases similares.

Usando la lista genérica puede tener una Lista de Lista de Lista lo que desee y siempre puede hacer referencia a la tipificación fuerte, no tiene que convertir o algo así como lo haría con una matriz o lista estándar.

Los generics le permiten usar tipeo fuerte para objetos y estructuras de datos que deberían poder contener cualquier objeto. También elimina los tipos de letra tediosos y caros al recuperar objetos de estructuras genéricas (boxeo / unboxing).

Un ejemplo que usa ambos es una lista vinculada. ¿De qué serviría una clase de lista enlazada si solo pudiera usar el objeto Foo? Para implementar una lista vinculada que pueda manejar cualquier tipo de objeto, la lista vinculada y los nodos en una clase interna de nodo hipotético deben ser generics si desea que la lista contenga solo un tipo de objeto.

Si su colección contiene tipos de valores, no es necesario que encapsen / desempaquen en los objetos cuando se insertan en la colección, por lo que su rendimiento aumenta dramáticamente. Complementos geniales como el reafilamiento pueden generar más código para usted, como los bucles foreach.

Otra ventaja de usar Generics (especialmente con Collections / Lists) es que obtiene Comprobación del tipo de tiempo de comstackción. Esto es realmente útil cuando se usa una Lista genérica en lugar de una Lista de objetos.

La razón más simple es que proporcionan seguridad de tipo

 List custCollection = new List; 

Opuesto a,

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

como un simple ejemplo.

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

This has several benefits for you:

  • Because the compiler 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 compile-time and you can fix the mistake before your testing-department files a cryptical bug report.

  • The compiler 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 compile 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 compiler depend on the types set at compile time. You parametrise your methods on the type you give at compile 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 compilers do the work during comstacktion. 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 parametrise the type and the code is generated by the compiler.

Once you get the idea of “compile time”, you can make use “bounded” types and restrict what can be passed as a parametrised 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(); } 

vs

 List stuffList = getStuff(); Iterator i = stuffList.iterator(); while(i.hasNext()) { Stuff stuff = (Stuff)i.next(); stuff.do(); } 

o

 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.