it-swarm-es.tech

¿Qué tiene de bueno los genéricos, por qué usarlos?

Pensé que le ofrecería este softbol a quien quisiera sacarlo del parque. ¿Qué son los genéricos, cuáles son las ventajas de los genéricos, por qué, dónde, cómo debo usarlos? Por favor, mantenlo bastante básico. Gracias.

79
MrBoJangles
  • Le permite escribir código o usar métodos de biblioteca que son seguros para el tipo, es decir, se garantiza que una Lista <cadena> es una lista de cadenas.
  • Como resultado de la utilización de los genéricos, el compilador puede realizar verificaciones en tiempo de compilación en el código para el tipo de seguridad, es decir, ¿está intentando incluir un int en esa lista de cadenas? El uso de un ArrayList causaría 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 valor a tipos de referencia o viceversa ) o la conversión de objetos al tipo de referencia requerido.
  • Le permite escribir código que sea aplicable a muchos tipos con el mismo comportamiento subyacente, es decir, un Diccionario <cadena, int> usa el mismo código subyacente que un Diccionario <DateTime, double>; Usando genéricos, el equipo marco solo tuvo que escribir una pieza de código para lograr ambos resultados con las ventajas mencionadas anteriormente.
120
ljs

Realmente odio repetirme. Odio escribir lo mismo más a menudo de lo que tengo que hacerlo. No me gusta repetir las cosas varias veces con ligeras diferencias.

En lugar de crear:

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

Puedo crear una clase reutilizable ... (en el caso de que no quieras usar la colección en bruto por alguna razón)

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

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

Esto también es cierto para las clases que no son de colección, como un Callable<T> o un Reference<T> que tiene que interactuar con otras clases. ¿Realmente desea extender Callable<T> y Future<T> y todas las demás clases asociadas para crear versiones de tipo seguro?

Yo no.

45
James Schek

No es necesario realizar una conversión tipográfica como una de las mayores ventajas de los genéricos de Java , ya que realizará la verificación de tipos en tiempo de compilación. Esto reducirá la posibilidad de que ClassCastExceptionname__s se pueda lanzar en el tiempo de ejecución, y puede llevar a un código más robusto.

Pero sospecho que eres plenamente consciente de eso.

Cada vez que miro los genéricos me da dolor de cabeza. Considero que la mejor parte de Java es su simplicidad y sintaxis mínima, y ​​los genéricos no son simples y agregan una cantidad significativa de nueva sintaxis.

Al principio, tampoco vi el beneficio de los genéricos. Comencé a aprender Java desde la sintaxis 1.4 (aunque Java 5 estaba disponible en ese momento) y cuando encontré genéricos, sentí que era más código para escribir, y realmente no entendía los beneficios.

Las IDE modernas facilitan la escritura de código con genéricos

La mayoría de las IDE modernas y decentes son lo suficientemente inteligentes como para ayudar a escribir código con genéricos, especialmente al completar el código.

Aquí hay un ejemplo de cómo hacer un Map<String, Integer> con un HashMapname__. El código que tendría que escribir es:

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

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

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

Es cierto que tenía que seleccionar HashMapde una lista de candidatos, pero básicamente IDE sabía qué agregar, incluidos los tipos genéricos. Con las herramientas adecuadas, el uso de genéricos no es tan malo.

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

Una ventaja clave de los genéricos proviene de la forma en que se desempeña bien con las nuevas características de Java 5. Este es un ejemplo de cómo lanzar enteros a Sety calcular su total :

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

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

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

Primero, los genéricos y el autoboxing de primitivas permiten las siguientes líneas:

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

El entero 10 se convierte automáticamente en un Integercon el valor de 10. (Y lo mismo para 42). Luego ese Integerse lanza a la Setque se sabe que contiene Integername__s. Tratar de lanzar un Stringcausaría un error de compilación.

A continuación, para cada bucle toma los tres:

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

Primero, el Setque contiene Integername__s se usa en un bucle para cada uno. Cada elemento se declara como un inty eso se permite cuando el Integervuelve a estar en la primitiva intname__. Y el hecho de que se produce este desempaquetado se conoce porque se usaron genéricos para especificar que Integername__s se mantuvo en Setname__.

Los genéricos pueden ser el pegamento que une las nuevas características introducidas en Java 5, y simplemente hace que la codificación sea más simple y segura. Y la mayoría de las veces, los IDE son lo suficientemente inteligentes como para ayudarlo con buenas sugerencias, por lo que en general no será mucho más que escribir.

Y, francamente, como se puede ver en el ejemplo de Setname__, creo que utilizar las características de Java 5 puede hacer que el código sea más conciso y sólido.

Editar - Un ejemplo sin genéricos

La siguiente es una ilustración del ejemplo anterior de Setsin el uso de genéricos. 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 sin marcar en tiempo de compilación)

Cuando se usan colecciones no genéricas, los tipos que se ingresan en la colección son objetos de tipo Objectname__. Por lo tanto, en este ejemplo, un Objectes lo que se está addname__ed en el conjunto.

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

En las líneas anteriores, el autoboxing está en juego: el valor primitivo int10 y 42 están siendo transferidos automáticamente a Integerobjetos, que se agregan al SetSin embargo, tenga en cuenta que los objetos Integerse manejan como Objectname__s, ya que no hay información de tipo para ayudar al compilador a saber qué tipo debe esperar Setname__.

for (Object o : set) {

Esta es la parte que es crucial. La razón por la que funciona el bucle for-each es que Setimplementa la interfaz IterableNAME _ , que devuelve un Iteratorcon información de tipo, si está presente. (Iterator<T>, eso es.)

Sin embargo, dado que no hay información de tipo, el Setdevolverá un Iteratorque devolverá los valores en el Setcomo Objectname__s, y es por eso que el elemento se recupera en el bucle para cada bucle must sea de tipo Objectname__.

Ahora que el Objectse recupera del Setname__, se debe convertir en un Integermanualmente para realizar la adición:

  total += (Integer)o;

Aquí, se realiza un encasillado desde un Objecta un Integername__. 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 encasilla es un ClassCastExceptionesperando que suceda, pero estoy divagando ...)

El Integerahora está sin caja en un inty se le permite realizar la adición en la intvariable totalname__.

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 claro y sencillo como escribir código con genéricos. Y, en mi opinión, para aprovechar al máximo las nuevas funciones de Java 5, uno debería estar buscando en los genéricos, si al menos permite verificaciones en tiempo de compilación para evitar que las predicciones no válidas puedan generar excepciones en tiempo de ejecución.

20
coobird

Si tuviera que buscar en la base de datos de errores de Java justo antes del lanzamiento de la versión 1.5, encontrará siete veces más errores con NullPointerException que con ClassCastException. Por lo tanto, no parece que sea una gran característica para encontrar errores, o al menos errores que persisten después de una pequeña prueba de humo.

Para mí, la gran ventaja de los genéricos es que documentan en código información importante de tipo. Si no quisiera que la información de ese tipo estuviera documentada en código, entonces usaría un lenguaje de tipo dinámico, o al menos un lenguaje con una inferencia de tipo más implícita.

Mantener las colecciones de un objeto en sí mismo no es un mal estilo (pero el estilo común es ignorar efectivamente la encapsulación). Depende más bien de lo que estás haciendo. Pasar las colecciones a "algoritmos" es un poco más fácil de verificar (en o antes del tiempo de compilación) con genéricos.

15

Los genéricos en Java facilitan el polimorfismo paramétrico . Por medio de parámetros de tipo, puedes 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 s, así un tipo como List<T> modela algún comportamiento, no solo para un tipo específico, sino para cualquier tipo. List<T> dice que para cualquier tipo T, hay un tipo de List cuyos elementos son Ts. Entonces List es en realidad un constructor de tipo. Toma un tipo como argumento y construye otro tipo como resultado.

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

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

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

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

Antes de los genéricos, el polimorfismo se logró mediante subclasificación usando la palabra clave extends. Con los genéricos, podemos eliminar las subclases y usar el polimorfismo paramétrico. Por ejemplo, considere una clase parametrizada (genérica) utilizada para calcular códigos hash para cualquier tipo. En lugar de reemplazar Object.hashCode (), usaríamos una clase genérica como esta:

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

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

Sin embargo, los genéricos de Java no son perfectos. Puede abstraer sobre tipos, pero no puede abstraer sobre tipos de constructores, por ejemplo. Es decir, puede decir "para cualquier tipo T", pero no puede decir "para cualquier tipo T que tome un parámetro de tipo A".

Escribí un artículo sobre estos límites de los genéricos de Java, aquí.

Una gran victoria con los genéricos es que te permiten evitar la subclasificación. La subclasificación tiende 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 tener en cuenta toda la jerarquía.

Wereas antes de los genéricos que podría tener clases como Widget extendido por FooWidget, BarWidget y BazWidget, con los genéricos se puede tener una sola Widget<A> clase genérica que toma un Foo, Bar o Baz en su constructor para darle Widget<Foo>, Widget<Bar> y Widget<Baz>.

10
Apocalisp

Los genéricos evitan el golpe de rendimiento del boxeo y el desempaquetado. Básicamente, mira ArrayList vs List <T>. Ambos hacen las mismas cosas básicas, pero la Lista <T> será mucho más rápida porque no tiene que encasillar desde/hasta el objeto.

8
Darren Kopp
  • Colecciones escritas: incluso si no desea usarlas, es probable que tenga que tratarlas desde otras bibliotecas, otras fuentes.

  • Tipificación genérica en la creación de clases:

    clase pública Foo <T> {public T get () ...

  • Evitar el casting - siempre me han disgustado cosas como

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

Esencialmente, cuando 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 genéricos fue similar a la tuya: "demasiado desordenada, demasiado complicada". Mi experiencia es que después de usarlos un poco, te acostumbras a ellos, y el código sin ellos se siente menos claramente especificado y es menos cómodo. Aparte de eso, el resto del mundo Java los utiliza, por lo que tendrá que trabajar con el programa, ¿no?

5
Steve B.

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

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

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

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

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

Ejemplo dos.

Ahora tiene sus dos listas de matrices y desea llamar a la función Bar () en cada una. Como hte ArrayList está llena de objetos, debes lanzarlos antes de que puedas llamar a la barra. Pero como la lista genérica de Foo solo puede contener Foos, puedes llamar a Bar () directamente sobre ellos.

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

foreach(Foo f in fl)
{
   f.Bar();
}
5
Peter Lange

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

Entonces, por ejemplo, en lugar de definir una estructura que consiste en una cadena y un entero, y luego tener que implementar un conjunto completo de objetos y métodos sobre cómo acceder a una matriz de esas estructuras, puede crear un Diccionario.

Dictionary<int, string> dictionary = new Dictionary<int, string>();

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

5
Tom Kidd

El mejor beneficio para los genéricos es la reutilización del código. Digamos que tiene muchos objetos de negocios y va a escribir un código MUY similar para que cada entidad realice las mismas acciones. (I.E Linq para operaciones SQL).

Con los genéricos, puede crear una clase que podrá operar dado cualquiera de los tipos que heredan de una clase base dada o implementar una interfaz dada, de esta manera:

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

    }
}

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 los genéricos para su trabajo, esta es mi favorita.

5
Dean Poulin

¿Nunca ha escrito un método (o una clase) en el que el concepto clave del método/clase no estuviera estrechamente vinculado a un tipo de datos específico de los parámetros/variables de instancia (piense en la lista vinculada, funciones máx/mín, búsqueda binaria) , etc.).

Nunca quisiste poder reutilizar el código/algorthm sin recurrir a la reutilización de cortar y pegar o comprometer la tipificación fuerte (por ejemplo, quiero un List de Strings, no un List de cosas I esperanza son ¡instrumentos de cuerda!)?

Por eso debesquererpara usar genéricos (o algo mejor).

4
Bert F

No olvide que los genéricos no solo son utilizados por las clases, también pueden ser usados ​​por métodos. Por ejemplo, tome el siguiente fragmento de código:

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

Es simple, pero puede ser utilizado muy elegantemente. Lo bueno es que el método devuelve lo que sea que fue dado. Esto ayuda cuando está manejando excepciones que deben ser devueltas a la persona que llama:

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

El punto es que no se pierde el tipo pasándolo a través de un método. Puede lanzar el tipo correcto de excepción en lugar de solo una Throwable, que sería todo lo que podría hacer sin los genéricos.

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

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

Esto no hace mucho, pero reduce el desorden cuando los tipos genéricos son largos (o están anidados; es decir, Map<String, List<String>>).

3
jigawot

De la documentación de Sun Java, en respuesta a "¿por qué debo usar los genéricos?":

"Generics proporciona una forma de comunicar el tipo de una colección al compilador, para que pueda verificarse. Una vez que el compilador conoce el tipo de elemento de la colección, el compilador puede verificar que haya usado la colección de manera consistente y que pueda insertar la conversión correcta de los valores que se extraen de la colección ... El código que usa los genéricos es más claro y seguro ... el compilador puede verificar en el momento de la compilación que las restricciones de tipo no se violan en el tiempo de ejecución [énfasis mío ]. Debido a que el programa compila sin advertencias, podemos afirmar con certeza que no emitirá una excepción ClassCastException en tiempo de ejecución. El efecto neto del uso de genéricos, especialmente en programas grandes, es mejor legibilidad y robustez . ] "

2
Demi

de todos modos, el JVM lanza ... crea implícitamente un código que trata al tipo genérico como "Objeto" y crea los lanzamientos para la creación de instancias deseada. Los genéricos de Java son solo azúcar sintáctica.

2
Anonymous Coward

La principal ventaja, como señala Mitchel, es la tipificación fuerte sin necesidad de definir múltiples clases.

De esta manera puedes hacer cosas como:

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

Sin los genéricos, tendría que lanzar blah [0] al tipo correcto para acceder a sus funciones.

2
Kevin Pang

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

Las colecciones de Java usan genéricos desde Java 1.5. Por lo tanto, un buen lugar para usarlos es cuando está creando su propio objeto de colección.

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

class Pair<F, S> {
    public final F first;
    public final S second;

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

Cada vez que utilice esta clase de par, puede especificar qué tipo de objetos desea tratar y cualquier problema de conversión de tipo aparecerá en el momento de la compilación, en lugar del tiempo de ejecución.

Los genéricos también pueden tener sus límites definidos con las palabras clave 'super' y 'extended'. Por ejemplo, si quieres lidiar con un tipo genérico pero quieres asegurarte de que extiende una clase llamada Foo (que tiene un método setTitle):

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

Si bien no es muy interesante por sí solo, es útil saber que cada vez que tratas con un FooManager, sabes que manejará los tipos de MyClass y que MyClass extiende Foo.

2
etchasketch

Los uso, por ejemplo, en un GenericDao implementado con SpringORM e Hibernate que tienen este aspecto

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

Mediante el uso de genéricos, mis implementaciones de este DAO obligan al desarrollador a pasarles solo las entidades para las que están diseñados simplemente mediante la subclasificación del 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
}

Mi pequeño marco es más robusto (tiene cosas como el filtrado, la carga perezosa, la búsqueda). Acabo de simplificar aquí para darles un ejemplo

Yo, como Steve y tú, dijimos al principio "Demasiado sucio y complicado" pero ahora veo sus ventajas

1
victor hugo

Los genéricos le permiten crear objetos fuertemente tipados, pero no tiene que definir el tipo específico. Creo que el mejor ejemplo útil es la Lista y clases similares.

Al usar la lista genérica, puede tener una lista de la lista que desee y siempre puede hacer referencia a la tipificación segura, no tiene que convertir nada como lo haría con una matriz o lista estándar.

1
Mitchel Sellers

Los genéricos le permiten usar la escritura con fuerza para objetos y estructuras de datos que deberían poder contener cualquier objeto. También elimina las tipificaciones tediosas y costosas al recuperar objetos de estructuras genéricas (boxeo/desempaquetado).

Un ejemplo que utiliza ambos es una lista enlazada. ¿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 genéricos si desea que la lista contenga solo un tipo de objeto.

1
Steve Landey

Los beneficios obvios como "seguridad de tipo" y "no casting" ya se mencionaron, así que tal vez pueda hablar sobre otros "beneficios" que espero ayuden.

En primer lugar, los genéricos son un concepto independiente del lenguaje y, en mi opinión, podría tener más sentido si piensa en el polimorfismo regular (tiempo de ejecución) al mismo tiempo.

Por ejemplo, el polimorfismo, tal como lo conocemos a partir del diseño orientado a objetos, tiene una noción de tiempo de ejecución en el que el objeto que llama se calcula en tiempo de ejecución a medida que avanza la ejecución del programa y se llama al método correspondiente según sea el tipo de tiempo de ejecución. En genéricos, la idea es algo similar pero todo sucede en tiempo de compilación. ¿Qué significa eso y cómo lo usas?

(Mantengámonos con métodos genéricos para mantenerlo compacto) Significa que aún puede tener el mismo método en clases separadas (como lo hizo anteriormente en clases polimórficas) pero esta vez son generadas automáticamente por el compilador dependiendo de los tipos establecidos en tiempo de compilación. Parametrizas tus métodos en el tipo que das en tiempo de compilación. Entonces, en lugar de escribir los métodos desde cero para cada tipo individual que tiene como lo hace en el polimorfismo en tiempo de ejecución (método que prevalece), deje que los compiladores hagan el trabajo durante la compilación. Esto tiene una ventaja obvia, ya que no necesita inferir todos los tipos posibles que podrían usarse en su sistema, lo que lo hace mucho más escalable sin un cambio de código.

Las clases funcionan de la misma manera. Se parametriza el tipo y el compilador genera el código.

Una vez que tenga la idea de "tiempo de compilación", puede hacer uso de los tipos "limitados" y restringir lo que se puede pasar como un tipo parametrizado a través de clases/métodos. Por lo tanto, puede controlar qué pasar a través de lo que es una cosa poderosa, especialmente si un marco está siendo consumido por otras personas.

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

Nadie puede establecer algo más que MyObject ahora.

Además, puede "imponer" restricciones de tipo en sus argumentos de método, lo que significa que puede asegurarse de que ambos argumentos de método dependan del mismo tipo.

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

Espero que todo esto tenga sentido.

1
stdout

Otra ventaja de usar los genéricos (especialmente con colecciones/listas) es que obtiene la verificación de tipo de tiempo de compilación. Esto es realmente útil cuando se utiliza una lista genérica en lugar de una lista de objetos.

1
Chris Pietschmann

Si su colección contiene tipos de valor, no es necesario que encajen/desajusten los objetos cuando se insertan en la colección para que su rendimiento aumente dramáticamente. Los complementos geniales como resharper pueden generar más código para ti, como bucles foreach.

1
gt124

Una de las razones principales es que proporcionan Tipo de seguridad

List<Customer> custCollection = new List<Customer>;

opuesto a,

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

como un simple ejemplo.

1
Vin

En resumen, los genéricos le permiten especificar con mayor precisión lo que pretende hacer (escribir más fuerte).

Esto tiene varios beneficios para ti:

  • Debido a que el compilador sabe más sobre lo que quiere hacer, le permite omitir muchos tipos de conversión de tipos porque ya sabe que el tipo será compatible.

  • Esto también le proporciona comentarios anteriores sobre las correcciones de su programa. Las cosas que anteriormente habrían fallado en el tiempo de ejecución (por ejemplo, porque un objeto no pudo ser lanzado en el tipo deseado), ahora fallan en tiempo de compilación y puede corregir el error antes de que su departamento de pruebas presente un informe de error críptico.

  • El compilador puede hacer más optimizaciones, como evitar el boxeo, etc.

1
Thomas Danecker

Un par de cosas para agregar/expandir (hablando desde el punto de vista de .NET):

Los tipos genéricos le permiten crear clases e interfaces basadas en roles. Esto ya se ha dicho en términos más básicos, pero me parece que empiezas a diseñar tu código con clases que se implementan de forma independiente, lo que da como resultado un código altamente reutilizable.

Los argumentos genéricos sobre los métodos pueden hacer lo mismo, pero también ayudan a aplicar el principio de "Decir No pedir", es decir, "dame lo que quiero y, si no puedes, dime por qué".

1
SpongeJim

Usar genéricos para colecciones es simple y limpio. Incluso si lo haces en cualquier otro lado, la ganancia de las colecciones es una victoria para mí.

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

o

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

Solo eso vale el "costo" marginal de los genéricos, y no tienes que ser un Gurú genérico para usar esto y obtener valor.

0
Will Hartung

Los genéricos también le brindan la capacidad de crear más objetos/métodos reutilizables y, al mismo tiempo, proporcionar soporte específico para cada tipo. También ganas mucho rendimiento en algunos casos. No conozco la especificación completa en Java Generics, pero en .NET puedo especificar restricciones en el parámetro Type, como Implements a Interface, Constructor, and Derivation.

0
Eric Schneider

Una vez di una charla sobre este tema. Puede encontrar mis diapositivas, código y grabación de audio en http://www.adventuresinsoftware.com/generics/ .

0
Michael L Perry