¿Qué es genérico? ¿Cuáles son los genéricos T, E, K, V? Este artículo te dice

Prefacio

Los genéricos de Java (genéricos) son una nueva característica introducida en JDK 5. Los genéricos proporcionan un mecanismo de detección de seguridad de tipos en tiempo de compilación, que permite a los desarrolladores detectar tipos ilegales en tiempo de compilación.

La esencia de los genéricos es un tipo parametrizado, lo que significa que el tipo de datos que se manipula se especifica como parámetro.


Los beneficios de los genéricos

En ausencia de genéricos, la "arbitrariedad" de los parámetros se logra mediante la referencia al tipo Objeto. La desventaja de la "arbitrariedad" es que se requiere una conversión de tipo explícita, y esta conversión requiere del desarrollador. El tipo de parámetro real se puede prever . En el caso de un error de conversión de tipo forzado, es posible que el compilador no genere un error y se produzca una excepción durante la operación, lo que constituye un riesgo de seguridad en sí mismo.

Entonces, la ventaja de los genéricos es que se puede verificar la seguridad de tipos en el momento de la compilación, y todas las conversiones son automáticas e implícitas.

Código de muestra:

public class GlmapperGeneric<T> {
    
      
    private T t;  
    public void set(T t) {
    
     this.t = t; }  
    public T get() {
    
     return t; }  
    
    public static void main(String[] args) {
    
      
        // do nothing  
    }  
  
  /**  
    * 不指定类型  
    */  
  public void noSpecifyType(){
    
      
    GlmapperGeneric glmapperGeneric = new GlmapperGeneric();  
    glmapperGeneric.set("test");  
    // 需要强制类型转换  
    String test = (String) glmapperGeneric.get();  
    System.out.println(test);  
  }  
  
  /**  
    * 指定类型  
    */  
  public void specifyType(){
    
      
    GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric();  
    glmapperGeneric.set("test");  
    // 不需要强制类型转换  
    String test = glmapperGeneric.get();  
    System.out.println(test);  
  }  
}  

El método specifyType en el fragmento de código anterior elimina la conversión forzada, que se puede verificar para la seguridad de tipos en tiempo de compilación y se puede usar en clases, métodos e interfaces.

Comodín genérico

Cuando definimos clases genéricas, métodos genéricos e interfaces genéricas, a menudo nos encontramos con muchos comodines diferentes, como T, E, K, V, etc. ¿Qué significan estos comodines?

T, E, K, V, de uso común
En esencia, todos estos son comodines, no hay diferencia, solo una convención al codificar. Por ejemplo, la T en el código anterior se puede reemplazar con cualquier letra entre AZ y no afectará el funcionamiento normal del programa, pero si se reemplaza con otra letra en lugar de T, la legibilidad puede ser más débil. "" Por lo general, T, E, K, V ,? Se acuerda así:

? "" Comodín ilimitado ""

Comencemos con un pequeño ejemplo.

Tengo una categoría principal Animal y varias subcategorías como perros, gatos, etc. Ahora necesito una lista de animales. Mi primera idea es la siguiente:

List<Animal> listAnimals  

Pero la idea del jefe es de hecho esta:

List<? extends Animal> listAnimals  

¿Por qué utilizar comodines en lugar de genéricos simples? Los comodines no tienen sentido cuando se declaran variables locales, pero cuando declaras un parámetro para un método, es muy importante.

static int countLegs (List<? extends Animal > animals ) {
    
      
    int retVal = 0;  
    for ( Animal animal : animals )  
    {
    
      
        retVal += animal.countLegs();  
    }  
    return retVal;  
}  
  
static int countLegs1 (List< Animal > animals ){
    
      
    int retVal = 0;  
    for ( Animal animal : animals )  
    {
    
      
        retVal += animal.countLegs();  
    }  
    return retVal;  
}  
  
public static void main(String[] args) {
    
      
    List<Dog> dogs = new ArrayList<>();  
  // 不会报错  
    countLegs( dogs );  
 // 报错  
    countLegs1(dogs);  
}  

Cuando se llama a countLegs1, será rojo, y el mensaje de error que aparece es el siguiente:
Inserte la descripción de la imagen aquí
Por lo tanto, para el tipo que es incierto o no le importa la operación real, puede usar comodines ilimitados (un signo de interrogación entre paréntesis angulares, que es <?>), lo que significa que puede mantener Hay cualquier tipo. Al igual que el método countLegs, la sesión anterior es limitada, pero no le importa cuál es el tipo específico, por lo que se pueden admitir todas las subclases de la pasada en Animal y no se informará ningún error. Pero countLegs1 no funcionará.

Comodín de límite superior <? Extiende E> Límite
superior: declarado con la palabra clave extiende, lo que indica que el tipo parametrizado puede ser el tipo especificado o una subclase de este tipo.

El uso de extensiones en el parámetro de tipo significa que el parámetro en este genérico debe ser una subclase de E o E. Esto tiene dos ventajas:

1、如果传入的类型不是 E 或者 E 的子类,编译不成功

2、泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
private <K extends A, E extends B> E test(K arg1, E arg2){
    
      
    E result = arg2;  
    arg2.compareTo(arg1);  
    //.....  
    return result;  
}

Si hay varios límites superiores de parámetros de tipo en la lista de parámetros de tipo, sepárelos con comas

Comodín inferior <? Super E>

Límite inferior: use super para declarar que el tipo parametrizado puede ser el tipo especificado, o el supertipo de este tipo, hasta Object

El uso de super en el parámetro de tipo significa que el parámetro en este tipo genérico debe ser E o la clase padre de E.

private <T> void test(List<? super T> dst, List<T> src){
    
      
    for (T t : src) {
    
      
        dst.add(t);  
    }  
}  

public static void main(String[] args) {
    
      
    List<Dog> dogs = new ArrayList<>();  
    List<Animal> animals = new ArrayList<>();  
    new Test3().test(animals,dogs);  
}  

// Dog 是 Animal 的子类  
class Dog extends Animal {
    
      
  
}  

El tipo dst es "mayor o igual que". El tipo de src. Aquí, "mayor o igual que" significa que el rango representado por dst es mayor que el de src, por lo que un contenedor que puede contener dst también puede contener src.

? Diferencia de T

Inserte la descripción de la imagen aquí
? Ambos y T representan tipos inciertos, la diferencia es que podemos operar en T, pero ¿no? No, como los siguientes:

// 可以  
T t = operate();  
  
// 不可以  
?car = operate();  

Para resumir brevemente:

¿T es un tipo determinado, generalmente utilizado en la definición de clases genéricas y métodos genéricos? Es un tipo indeterminado, generalmente se usa para el código de llamada y parámetros formales de métodos genéricos, y no se puede usar para definir clases y métodos genéricos.

Diferencia 1: use T para garantizar la coherencia de los parámetros genéricos

// 通过 T 来 确保 泛型参数的一致性  
public <T extends Number> void  
test(List<T> dest, List<T> src)  
  
//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型  
public void  
test(List<? extends Number> dest, List<? extends Number> src)  

Como en el código siguiente, la T acordada es una subclase de Number, pero se usa String en la declaración, por lo que se informará un error.
Inserte la descripción de la imagen aquí
No hay garantía de que dos listas tengan el mismo tipo de elemento

GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric<>();  
List<String> dest = new ArrayList<>();  
List<Number> src = new ArrayList<>();  
glmapperGeneric.testNon(dest,src);  

El código anterior no informará de un error en el compilador, pero al ingresar a la operación interna del método testNon (como la asignación), para dest y src, la conversión de tipo aún es necesaria.


Diferencia 2: Los parámetros de tipo pueden ser calificados múltiples, pero los comodines no.
Inserte la descripción de la imagen aquí
Utilice el símbolo & para establecer límites múltiples (límites múltiples). El tipo genérico especificado T debe ser un subtipo común de MultiLimitInterfaceA y MultiLimitInterfaceB. En este momento, la variable t tiene todo los métodos y atributos limitados. Para los comodines, debido a que no es un tipo determinado, no puede ser calificado múltiple


Diferencia 3: Los comodines pueden ser calificados por superclases, pero los parámetros de tipo no. El parámetro de tipo T solo tiene un método de calificación de tipo:
T extends A  

Pero el comodín se puede restringir de dos formas:

? extends A  
? super A  

La diferencia entre clase y clase <?>

¿Introducido antes? La diferencia con T, entonces, ¿cuál es la diferencia entre Class y <Class <?>? Clase y Clase <?>

El más común es el uso en la escena de reflexión, aquí hay un código de emisión para ilustrarlo.

// 通过反射的方式生成  multiLimit   
// 对象,这里比较明显的是,我们需要使用强制类型转换  
MultiLimit multiLimit = (MultiLimit)  
Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();  

Para el código anterior, en tiempo de ejecución, si el tipo reflejado no es una clase MultiLimit, definitivamente informará un error java.lang.ClassCastException.

En este caso, puede utilizar el siguiente código en su lugar, de modo que los problemas de tipos se puedan comprobar directamente en el momento de la compilación:

Inserte la descripción de la imagen aquí
Cuando se crea una instancia de Class, T debe reemplazarse con una clase concreta. Clase <?> Es un tipo genérico comodín, puede representar cualquier tipo, por lo que se usa principalmente para la restricción de declaración. Por ejemplo, podemos declarar así:

// 可以  
public Class<?> clazz;  
// 不可以,因为 T 需要指定类型  
public Class<T> clazzT;  

Entonces, cuando no sepa qué tipo de Clase declarar, puede definir una Clase <?>.

Si también desea public Class clazzT; en este caso, también debe especificar T para la clase actual,

public class Test3<T> {
    
      
    public Class<?> clazz;  
    // 不会报错  
    public Class<T> clazzT;  

Supongo que te gusta

Origin blog.csdn.net/qq_36551991/article/details/111656215
Recomendado
Clasificación