Conceptos básicos de Java (1): uso sencillo de genéricos

1. Definición y funciones de los genéricos.

Los genéricos son un mecanismo de programación que permite el uso de tipos parametrizados al escribir código para lograr seguridad de tipos en tiempo de compilación. El siguiente es el papel de los genéricos:

  1. Mejore la legibilidad y el mantenimiento del código : al utilizar parámetros genéricos en su código, puede hacerlo más claro, más legible y más fácil de mantener.

  2. Seguridad del código mejorada : los genéricos pueden verificar los tipos en tiempo de compilación, evitando errores de conversión de tipos en tiempo de ejecución.

  3. Mayor reutilización del código : los genéricos permiten escribir código común en diferentes tipos de datos, aumentando así la reutilización del código.

  4. Simplifique el código : el uso de genéricos puede simplificar el código evitando escribir código similar repetidamente.

En resumen, los genéricos son un poderoso mecanismo de programación que puede ayudar a los desarrolladores a escribir código que sea más legible, mantenible, seguro y reutilizable.

Los genéricos de Java realizan el borrado de tipos (Type Erasure) durante la compilación. El borrado de tipo significa que cuando el compilador compila código genérico, borrará el tipo genérico a su tipo original o tipo calificado, de modo que el tipo genérico no exista en tiempo de ejecución. Al realizar el borrado de tipos, el compilador reemplaza los parámetros de tipo genérico con su tipo delimitador (calificado) (si lo hay), o con el tipo de objeto si no se especifica ningún tipo calificado.

Por ejemplo, una clase genérica se define de la siguiente manera:

public class Box<T> {
    
    
    private T content;
    public void setContent(T content) {
    
    
        this.content = content;
    }
    public T getContent() {
    
    
        return content;
    }
}

Después de la compilación, el borrado de tipos borrará el tipo genérico T al tipo de Objeto, y el código de bytes generado es el siguiente:

public class Box {
    
    
    private Object content;
    public void setContent(Object content) {
    
    
        this.content = content;
    }
    public Object getContent() {
    
    
        return content;
    }
}

Cabe señalar que, aunque el tipo genérico no existe en tiempo de ejecución, el compilador aún verifica el tipo genérico durante la compilación para garantizar la seguridad de los tipos.

2. Uso de genéricos

1. Los genéricos se definen en la clase.

El código se muestra a continuación:

class CommonUtil {
    
    

  public Object obj;

  public Object getObj() {
    
    
    return obj;
  }

  public void setObj(Object obj) {
    
    
    this.obj = obj;
  }
}

Al usarlo, el código es el siguiente:

  public static void main(String[] args) {
    
    

    CommonUtil tool = new CommonUtil<>();
    
    tool.setObj(2);
    Integer val1 = (Integer)tool.getObj();
    
	tool.setObj("hello java");
    String val2 = (String)tool.getObj();
  }

Como se puede ver en el código anterior, se requiere una conversión forzada cada vez que se obtiene el valor. Los errores de conversión forzada pueden ocurrir fácilmente si no se tiene cuidado. Entonces, ¿hay alguna manera de evitar la coerción de tipos? La respuesta es: genéricos. Al definir genéricos en una clase, puede evitar la conversión de tipos. El código mejorado es el siguiente:

class CommonUtil<T> {
    
    

  public T obj;

  public T getObj() {
    
    
    return obj;
  }

  public void setObj(T obj) {
    
    
    this.obj = obj;
  }
}

Al crear una instancia, pase el tipo específico. El código es el siguiente:

  public static void main(String[] args) {
    
    

    CommonUtil<Integer> tool = new CommonUtil<>();
    tool.setObj(2);
    Integer val1 = tool.getObj();
    
    CommonUtil<String> tool = new CommonUtil<>();
	tool.setObj("hello java");
    String val2 = tool.getObj();
  }

2. Método de definición genérico

Hay un punto no tan bueno sobre la definición genérica de la clase. Echa un vistazo al siguiente ejemplo. El código es el siguiente:

class CommonUtil<T> {
    
    

  public void show(T obj){
    
    
    System.out.println("obj = " + obj);
  }
}

El código de uso del código es el siguiente:

    CommonUtil<Integer> tool1 = new CommonUtil<>();
    tool1.show(value);

    CommonUtil<String> tool2 = new CommonUtil<>();
    tool2.show("111");
    
	CommonUtil<Person> tool2 = new CommonUtil<>();
    tool2.show(new Person());

¿Descubrió que cada vez que llama al método show(), necesita crear una instancia, porque puede especificar el tipo de parámetro específico al crear? Pero crear objetos de esta manera es demasiado problemático. ¿Cómo solucionarlo? Puedes definir genéricos en los métodos. El código es el siguiente:

class CommonUtil {
    
    

  public <W> void show(W obj){
    
    
    System.out.println("obj = " + obj);
  }
}

El código de uso es el siguiente:

    CommonUtil tool1 = new CommonUtil<>();
    tool1.show(value);
    tool1.show("111");
    tool1.show(new Person());

Finalmente, el tipo de parámetro específico se pasa cuando se llama al método, de modo que se puede evitar la creación repetida de objetos y se puede llamar a un método repetidamente, lo cual es muy similar a Object. También se pueden utilizar métodos estáticos que incluyen modificación estática . El código se muestra a continuación:

class CommonUtil {
    
    

  public static <W> void show(W obj){
    
    
    System.out.println("obj = " + obj);
  }
}

El código de uso es el siguiente:

    CommonUtil.show(value);
    CommonUtil.show("111");
    CommonUtil.show(new Person());

Sin embargo, al definir genéricos en métodos estáticos, tenga en cuenta que este método estático no puede utilizar genéricos en la clase. ¿Por qué? Debido a que el tipo de parámetro específico se especifica cuando se crea la clase y el método estático se carga en la JVM antes de crear una instancia de la clase, no hay forma de saber qué tipo de parámetro específico pasó su clase al crear una instancia. Ejemplos de errores son los siguientes:

class CommonUtil<W> {
    
    

  public static <W> void show(W obj){
    
    
    System.out.println("obj = " + obj);
  }
}

3. Los genéricos se definen en la interfaz.

A veces, los genéricos se definen en interfaces y el código es el siguiente:

public interface Inter<T> {
    
    

	public void print(T obj);
}

Los genéricos en la interfaz se pueden especificar en la subclase o no. El tipo específico especificado por la subclase es el siguiente:

public InterImpl implements Inter<String> {
    
    
	// 接口上的方法
	public void print(String obj){
    
    }
	// 子类独有方法
	protect void show(Object obj){
    
    }
}

Cuando está en uso, el código es el siguiente:

	Inter<String> inter = new InterImpl();
	inter.print("hello");
	
	InterImpl impl = new InterImpl();
	impl.show(new Object());

O defina genéricos en subclases e interfaces para definir tipos específicos, el código es el siguiente:

public InterImpl<W> implements Inter<String> {
    
    
	// 接口上的方法
	public void print(String obj){
    
    }
	// 子类独有方法
	protect void show(W obj){
    
    }
}

Cuando está en uso, el código es el siguiente:

	Inter<String> inter = new InterImpl();
	inter.print("hello");
	
	InterImpl<Integer> impl = new InterImpl();
	impl.show(new Integer(10));

Si el tipo específico no se conoce en la subclase, también puede definir un genérico y pasar el genérico de la subclase a la interfaz. El código es el siguiente:

public InterImpl<W> implements Inter<W> {
    
    
	// 接口上的方法
	public void print(W obj){
    
    }
	// 子类独有方法
	protect void show(W obj){
    
    }
}

Cuando está en uso, el código es el siguiente:

	Inter<String> inter = new InterImpl();
	inter.print("hello");
	
	InterImpl<Integer> impl = new InterImpl();
	impl.show(new Integer(10));

4. Comodines genéricos

Por ejemplo, el código es el siguiente:

public class FanxinDemo {
    
    

  public static void print(Collection<String> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

  public static void main(String[] args) {
    
    

    List<String> list1 = new ArrayList<>();
    list1.add("a");
    list1.add("b");
    list1.add("c");

    List<Integer> list2 = new ArrayList<>();
    list2.add(1);
    list2.add(2);

    print(list1);
 }
}

Se puede encontrar que el método print () solo puede generar tipos de parámetros de cadena, pero no otros parámetros ¿Cómo se puede compartir este código? Aquí hay un comodín genérico ?. Cuando no sepas qué tipo es un genérico, puedes usar esta representación temporal. El código es el siguiente:

public class FanxinDemo {
    
    

  public static void print(Collection<?> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

  public static void main(String[] args) {
    
    

    List<String> list1 = new ArrayList<>();
    list1.add("a");
    list1.add("b");
    list1.add("c");

    List<Integer> list2 = new ArrayList<>();
    list2.add(1);
    list2.add(2);

    print(list1);
    print(list2);
 }
}

Pero supongamos que no desea que el método print() sea utilizado por parámetros de tipo entero, ¿qué debe hacer? Esto requiere una calificación genérica.

5. Limitación genérica

La calificación genérica puede limitar el rango de tipos de parámetros. ?Este rango de niveles es demasiado grande e inseguro. El rango está limitado por las palabras clave extends y super. Sin embargo, estas dos palabras clave solo pueden ser de herencia única, por lo que también están limitadas. Hay dos tipos de restricciones: ponerlo a disposición de todas las clases principales y ponerlo a disposición de todas las subclases.

5.1 Ponerlo a disposición de todas las clases para padres

El código se muestra a continuación:

  public static void print(Collection<? super Integer> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

Indica que todas las clases principales de Integer pueden usar el método print().

5.2 Ponerlo a disposición de todas las subclases

El código se muestra a continuación:

  public static void print(Collection<? extends String>> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

Indica que todas las subclases de String pueden usar el método print().

Supongo que te gusta

Origin blog.csdn.net/qq_35971258/article/details/129031946
Recomendado
Clasificación