Pregunta

Pensé en ofrecer esta pelota de softbol a quien quisiera sacarla 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.

¿Fue útil?

Solución

  • Le permite escribir código/usar métodos de biblioteca que sean seguros para tipos, es decir,Se garantiza que una Lista<cadena> será una lista de cadenas.
  • Como resultado del uso de genéricos, el compilador puede realizar comprobaciones en tiempo de compilación del código para garantizar la seguridad de los tipos, es decir,¿Estás intentando poner un int en esa lista de cadenas?El uso de ArrayList provocaría que se tratara de 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 convertir 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<cadena, int> usa el mismo código subyacente que un Diccionario<DateTime, double>;Al utilizar genéricos, el equipo del marco solo tuvo que escribir un fragmento de código para lograr ambos resultados con las ventajas antes mencionadas.

Otros consejos

Realmente odio repetirme.Odio escribir lo mismo más a menudo de lo necesario.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 construir una clase reutilizable...(en el caso de que no desee utilizar la colección sin formato por algún motivo)

class MyList<T> {
   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 válido para clases que no son de colección, como Callable<T> o un Reference<T> que tiene que interactuar con otras clases.¿Realmente quieres extender Callable<T> y Future<T> ¿Y todas las demás clases asociadas para crear versiones con seguridad de tipos?

No.

No tener que encasillar es una de las mayores ventajas de los genéricos de Java., ya que realizará una verificación de tipos en tiempo de compilación.Esto reducirá la posibilidad de ClassCastExceptions que pueden lanzarse en tiempo de ejecución y pueden generar un código más sólido.

Pero sospecho que eres plenamente consciente de ello.

Cada vez que miro los genéricos me da dolor de cabeza.Creo que la mejor parte de Java es su simplicidad y la 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 ya estaba disponible en ese momento) y cuando encontré los genéricos, sentí que había más código para escribir y realmente no entendía los beneficios.

Los IDE modernos facilitan la escritura de código con genéricos.

La mayoría de los IDE modernos y decentes son lo suficientemente inteligentes como para ayudar a escribir código con genéricos, especialmente con la finalización del código.

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

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

Y, de hecho, hay mucho que escribir sólo para crear una nueva HashMap.Sin embargo, en realidad, sólo tuve que escribir esto antes de que Eclipse supiera lo que necesitaba:

Map<String, Integer> m = new Ha Control+Espacio

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

Además, dado que se conocen los tipos, al recuperar elementos de la colección genérica, el IDE actuará como si ese objeto ya fuera un objeto de su tipo declarado; no es necesario realizar una conversión para que el IDE sepa cuál es el tipo de objeto. es.

Una ventaja clave de los genéricos proviene de la forma en que funcionan bien con las nuevas funciones de Java 5. A continuación se muestra un ejemplo de cómo arrojar números enteros a un Set y calculando 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 numero entero 10 está empaquetado automáticamente en un Integer con el valor de 10.(Y lo mismo para 42).Entonces eso Integer es arrojado al Set que se sabe que sostiene Integers.Tratando de lanzar un String causaría un error de compilación.

A continuación, el bucle for-each toma los tres:

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

Primero el Set que contiene Integers se utilizan en un bucle para cada uno.Cada elemento se declara como un int y eso está permitido como Integer se desempaqueta de nuevo a la primitiva int.Y el hecho de que ocurra este unboxing se sabe porque se usó genéricos para especificar que había Integerse lleva a cabo en el Set.

Los genéricos pueden ser el pegamento que reúne las nuevas características introducidas en Java 5 y simplemente hacen 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á necesario escribir mucho más.

Y, francamente, como se puede ver en el Set Por ejemplo, creo que utilizar las funciones 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 de lo anterior. Set ejemplo sin el uso de genéricos.Es posible, pero no es precisamente 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 compilación).

Cuando se utilizan 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 esta siendo added en el conjunto.

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

En las líneas anteriores, está en juego el autoboxing: el primitivo int valor 10 y 42 están siendo autoboxeados en Integer objetos que se van añadiendo al Set.Sin embargo, tenga en cuenta, el Integer Los objetos se manejan como Objects, ya que no hay información de tipo para ayudar al compilador a saber qué tipo tiene el Set debería esperar.

for (Object o : set) {

Esta es la parte que es crucial.La razón por la que el bucle for-each funciona es porque el Set implementa el Iterable interfaz, que devuelve un Iterator con información de tipo, si está presente.(Iterator<T>, 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 Objects, 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 de la Set, es necesario convertirlo en un Integer manualmente para realizar la suma:

  total += (Integer)o;

Aquí, se realiza un encasillamiento desde un Object a una Integer.En este caso, sabemos que esto siempre funcionará, pero el encasillamiento 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 encasillamiento es un ClassCastException esperando que suceda, pero estoy divagando...)

El Integer ahora está sin empaquetar en un int y se le permitió realizar la adición en el int variable total.

Espero poder ilustrar que las nuevas características de Java 5 se pueden usar con código no genérico, pero no es tan limpio y directo como escribir código con genéricos.Y, en mi opinión, para aprovechar al máximo las nuevas características de Java 5, uno debería buscar genéricos, si al menos permiten verificaciones en tiempo de compilación para evitar encasillamientos no válidos que generen excepciones en tiempo de ejecución.

Si buscara en la base de datos de errores de Java justo antes del lanzamiento de 1.5, encontraría siete veces más errores con NullPointerException que 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 codigo información de tipo importante.Si no quisiera que esa información de tipo estuviera documentada en código, entonces usaría un lenguaje escrito dinámicamente, o al menos un lenguaje con una inferencia de tipos 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 efectivamente 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 el momento de la compilación o antes) con los genéricos.

Los genéricos en Java facilitan polimorfismo paramétrico.Mediante parámetros de tipo, puede pasar argumentos a tipos.Así como un método como String foo(String s) modela algún comportamiento, no sólo para una cadena en particular, sino para cualquier cadena s, entonces un tipo como List<T> modela algún comportamiento, no sólo 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 tipos.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 requiere un A y devuelve un B. Cuando implementas esta interfaz, A y B Puede ser cualquier tipo que desee, siempre y cuando proporcione una función. f que toma el primero y devuelve el segundo.A continuación se muestra 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 lograba mediante subclasificación utilizando el extends palabra clave.Con los genéricos, podemos eliminar las subclases y utilizar polimorfismo paramétrico en su lugar.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<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, porque podemos seguir con el tema del uso de composición y polimorfismo paramétrico sin bloquear jerarquías frágiles.

Sin embargo, los genéricos de Java no son perfectos.Puedes abstraer tipos, pero no puedes abstraer constructores de tipos, 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 ventaja de los genéricos es que te permiten evitar la subclasificación.La subclasificación tiende a dar lugar a jerarquías de clases frágiles que resultan difíciles de ampliar y clases que son difíciles de entender individualmente sin observar la jerarquía completa.

Antes de los genéricos, es posible que tuvieras clases como Widget extendido por FooWidget, BarWidget, y BazWidget, con los genéricos puedes tener una única clase genérica Widget<A> eso toma un Foo, Bar o Baz en su constructor para darte Widget<Foo>, Widget<Bar>, y Widget<Baz>.

Los genéricos evitan el impacto en el rendimiento del boxeo y el unboxing.Básicamente, mire ArrayList vs List<T>.Ambos hacen las mismas cosas principales, pero List<T> será mucho más rápido porque no es necesario encajonar hacia/desde el objeto.

Simplemente me gustan porque te brindan una forma rápida de definir un tipo personalizado (ya que 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., puedes simplemente 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).

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

Con los genéricos, puede crear una clase que podrá operar con cualquiera de los tipos que heredan de una clase base determinada o implementar una interfaz determinada 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 genéricos en tu trabajo, esta es mi favorita.

  • Colecciones mecanografiadas: incluso si no desea utilizarlas, es probable que tenga que tratar con ellas desde otras bibliotecas y 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 (objeto o) {if (o instancia de clásicoeAbout) ...

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

Mi reacción inicial ante los genéricos fue similar a la suya: "demasiado complicado, demasiado complicado".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 menos cómodo.Aparte de eso, el resto del mundo Java los usa, por lo que eventualmente tendrás que usar el programa, ¿verdad?

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

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

Ejemplo 1Ahora quieres tener una colección de objetos Foo.Tiene dos opciones, LIst o ArrayList, y ambas 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 la agregará, pero la Lista genérica de Foo provocará que se genere una excepción.

Ejemplo dos.

Ahora tiene sus dos listas de matrices y desea llamar a la función Bar() en cada una.Dado que ArrayList está lleno de objetos, debe convertirlos antes de poder 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();
}

¿Nunca ha escrito un método (o una clase) donde 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 lista enlazada, funciones máx/mín, búsqueda binaria? , etc.).

¿Nunca ha deseado poder reutilizar el algoritmo/código sin recurrir a la reutilización de cortar y pegar o comprometer la escritura segura (p. ej.quiero un List de cuerdas, no un List de las cosas que yo esperanza son cuerdas!)?

Por eso deberías desear usar genéricos (o algo mejor).

No olvide que los genéricos no solo los usan las clases, sino que también pueden usarse los métodos.Por ejemplo, tome el siguiente fragmento:

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

Es simple, pero se puede usar de manera muy elegante.Lo bueno es que el método devuelve lo que se le dio.Esto ayuda cuando maneja excepciones que deben volver a enviarse a la persona que llama:

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

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

Este es sólo un ejemplo sencillo de un uso de métodos genéricos.Hay bastantes otras cosas interesantes que puedes hacer con métodos genéricos.Lo mejor, en mi opinión, es la inferencia de tipos con genéricos.Tome el siguiente ejemplo (tomado de la segunda edición de Effective Java 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 algo de desorden cuando los tipos genéricos son largos (o anidados;es decir. Map<String, List<String>>).

La principal ventaja, como señala Mitchel, es la tipificación segura sin necesidad de definir varias clases.

De esta manera puedes hacer cosas como:

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

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

el jvm lanza de todos modos...implícitamente crea código que trata el tipo genérico como "Objeto" y crea conversiones a la instanciación deseada.Los genéricos de Java son simplemente azúcar sintáctico.

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

Uso de colecciones de Java genéricos desde Java 1.5.Entonces, un buen lugar para usarlos es cuando estás creando tu propio objeto similar a una colección.

Un ejemplo que veo en casi todas partes es una clase Pair, que contiene dos objetos, pero necesita tratar con 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;   
    }
}  

Siempre que utilice esta clase Pair, puede especificar con qué tipo de objetos desea tratar y cualquier problema de conversión de tipos aparecerá en el tiempo de compilación, en lugar de en el tiempo de ejecución.

Los genéricos también pueden tener sus límites definidos con las palabras clave 'super' y 'extends'.Por ejemplo, si quiere trabajar con un tipo genérico pero quiere asegurarse de que extienda 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 trabaja con un FooManager, sabe que manejará los tipos MyClass y que MyClass extiende Foo.

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

"Los genéricos proporcionan una forma de comunicar el tipo de una colección al compilador, para que pueda comprobarse.Una vez que el compilador conoce el tipo de elemento de la colección, puede verificar que haya utilizado la colección de manera consistente y puede insertar las conversiones correctas en los valores que se eliminan de la colección...El código que utiliza genéricos es más claro y seguro.... el compilador puede verificar en tiempo de compilación que las restricciones de tipo no se violan en tiempo de ejecución [el énfasis es mío].Debido a que el programa se compila sin advertencias, podemos afirmar con certeza que no generará una ClassCastException en tiempo de ejecución.El efecto neto del uso de genéricos, especialmente en programas grandes, es legibilidad y robustez mejoradas.[el énfasis es mío]"

Los genéricos le permiten crear objetos fuertemente tipados, pero no es necesario definir el tipo específico.Creo que el ejemplo más útil es List y clases similares.

Usando la lista genérica puedes tener una Lista Lista lo que quieras y siempre puedes hacer referencia a la escritura segura, no tienes que convertir ni nada como lo harías con una Matriz o una Lista estándar.

Los genéricos le permiten utilizar tipos fuertes para objetos y estructuras de datos que deberían poder contener cualquier objeto.También elimina encasillamientos tediosos y costosos al recuperar objetos de estructuras genéricas (boxing/unboxing).

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

Si su colección contiene tipos de valores, no es necesario encuadrarlos o desempaquetarlos en objetos cuando se insertan en la colección, por lo que su rendimiento aumenta drásticamente.Los complementos interesantes como resharper pueden generar más código para usted, como bucles foreach.

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

La principal razón es que proporcionan Tipo de seguridad

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

Opuesto a,

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

como un ejemplo sencillo.

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

Esto tiene varios beneficios para ti:

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

  • Esto también le brinda comentarios más tempranos sobre la corrección de su programa.Cosas que anteriormente habrían fallado en tiempo de ejecución (p. ej.porque un objeto no se pudo convertir en el tipo deseado), ahora falla 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 realizar más optimizaciones, como evitar el boxeo, etc.

Un par de cosas para agregar/ampliar (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 encuentro que comienzas a diseñar tu código con clases que se implementan de forma independiente del tipo, 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 preguntar", al casting, es decir,"dame lo que quiero, y si no puedes, dime por qué".

Los uso, por ejemplo, en un GenericDao implementado con SpringORM e Hibernate que se ve así

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

Al usar genéricos, mis implementaciones de estos DAO obligan al desarrollador a pasarles solo las entidades para las que están diseñados simplemente subclasificando 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 filtrado, carga diferida, búsqueda).Simplemente lo simplifiqué aquí para darte un ejemplo.

Yo, como Steve y tú, dije al principio. "Demasiado desordenado y complicado" pero ahora veo sus ventajas

Ya se mencionan beneficios obvios como "seguridad de tipos" y "sin lanzamiento", así que tal vez pueda hablar sobre algunos otros "beneficios" que espero que 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 piensas en el polimorfismo regular (en tiempo de ejecución) al mismo tiempo.

Por ejemplo, el polimorfismo, como lo conocemos por el diseño orientado a objetos, tiene una noción de tiempo de ejecución en la que el objeto que llama se determina en tiempo de ejecución a medida que avanza la ejecución del programa y se llama al método relevante en consecuencia dependiendo del 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 se utiliza?

(Sigamos 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 el compilador los genera automáticamente y dependen de los tipos establecidos. en tiempo de compilación.Usted parametriza sus métodos según el tipo que proporciona en el momento de la compilación.Entonces, en lugar de escribir los métodos desde cero para cada tipo tiene como lo hace en el polimorfismo en tiempo de ejecución (anulación de métodos), deja 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 necesidad de cambiar el código.

Las clases funcionan prácticamente de la misma manera.Usted parametriza el tipo y el compilador genera el código.

Una vez que tenga la idea del "tiempo de compilación", puede utilizar tipos "limitados" y restringir lo que se puede pasar como un tipo parametrizado a través de clases/métodos.Por lo tanto, puedes controlar lo que se pasará, lo cual es algo poderoso, especialmente si otras personas consumen un marco.

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

Nadie puede configurar nada más que MyObject ahora.

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

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

Espero que todo esto tenga sentido.

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

Usar genéricos para colecciones es simple y limpio.Incluso si lo apuestas en todos los demás lugares, 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 "coste" marginal de los genéricos, y no es necesario ser un gurú de los genéricos para usarlos y obtener valor.

Los genéricos también le brindan la posibilidad de crear más objetos/métodos reutilizables y, al mismo tiempo, brindan soporte para tipos específicos.También se gana mucho rendimiento en algunos casos.No conozco las especificaciones completas de Java Generics, pero en .NET puedo especificar restricciones en el parámetro Tipo, como Implementa una interfaz, Constructor y Derivación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top