Genéricos de Java (incluidas las aplicaciones de genéricos + reflexión)

Introducción a los genéricos

Java generics ( generics) es una nueva característica introducida en JDK 5. Los genéricos proporcionan un 编译时类型安全检测机制mecanismo que permite a los programadores detectar tipos ilegales en tiempo de compilación.

La esencia de los genéricos es 参数化类型, es decir , que el tipo de datos que se manipula se especifica como uno 参数. El tipo específico solo se especifica cuando se crea el objeto o se llama al método.

Tipo de borrado

El primer requisito previo para una correcta comprensión de los conceptos genéricos es la comprensión 类型擦除.

Para obtener más información sobre el borrado de tipo, puede consultar este artículo: "Borrado de tipo genérico de Java y los problemas causados ​​por el borrado de tipo "

¿Qué es 类型擦除?

El tipo genérico de 伪泛型Java se debe a que toda la información genérica se borrará durante la compilación de Java. Los genéricos de Java se implementan básicamente en el nivel del compilador, y el código de bytes generado no contiene la información de tipo en los genéricos.

  • Cuando utilice genéricos, agregue parámetros de tipo y los parámetros se eliminarán cuando el compilador compile Se llama a este proceso 类型擦除.

Por ejemplo List<String>, los tipos definidos en el código pasarán a ser después de la compilación. ListLa información de tipo adjunta por el tipo genérico es invisible para la JVM. La JVM solo ve la Lista. El compilador de Java encontrará posibles errores tanto como sea posible en tiempo de compilación, pero aún no puede generar excepciones de conversión de tipos en tiempo de ejecución. El borrado de tipos también es 模板机制una diferencia importante entre los genéricos de Java y la implementación de C ++ .

Comodín

Para obtener más información sobre los comodines en los genéricos de Java, puede consultar este artículo: "¿Hablemos de los comodines T, E, K, V en los genéricos de Java ?

Los comodines más utilizados son: T, E, K, V ,?

  • ? Representa un tipo de Java incierto
  • T Representa un tipo específico de Java (Tipo)
  • K, VRepresentar respectivamente el valor clave en el valor clave de Java
  • E Representa elemento

Tres formas de usar genéricos

Los genéricos son generalmente de tres 泛型类maneras: 泛型接口, 泛型方法,.

Clase genérica

Esto Tse puede escribir como cualquier identificador. Los parámetros comunes como T, E, K y V se utilizan a menudo para representar genéricos.

Al crear una instancia de una clase genérica, debe especificar el tipo específico de T.

public class Generic<T>{
    
     

    private T key;

    public Generic(T key) {
    
     
        this.key = key;
    }

    public T getKey(){
    
     
        return key;
    }
}

Crea una instancia de la clase genérica:

Generic<Integer> genericInteger = new Generic<Integer>(123456);

Interfaz genérica

public interface Generator<T> {
    
    
    public T method();
}
  1. Implemente una interfaz genérica sin especificar el tipo:
class GeneratorImpl<T> implements Generator<T>{
    
    
    @Override
    public T method() {
    
    
        return null;
    }
}
  1. Implemente una interfaz genérica, especifique el tipo:
class GeneratorImpl<T> implements Generator<String>{
    
    
    @Override
    public String method() {
    
    
        return "hello";
    }
}

Método genérico

public static <E> void printArray(E[] inputArray){
    
             
    for(E element : inputArray){
    
            
      	System.out.printf( "%s ", element );
    }
    System.out.println();
}

utilizar:

// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = {
    
     1, 2, 3 };
String[] stringArray = {
    
     "Hello", "World" };
printArray(intArray); 
printArray(stringArray); 

Ventajas de los genéricos

Las ventajas de usar genéricos son:

  1. El código es más conciso, no es necesario forzar la conversión de tipo

  2. El programa es más robusto, no hay advertencias durante la compilación ni ClassCastExceptionexcepciones durante el tiempo de ejecución.

  3. Mejorar la reutilización del código, como: en proxy dinámico, a través de genéricos puede proxy cualquier clase que necesite ser proxy.

public class ServiceProxy<T> implements InvocationHandler {
    
    
  	private T target;
  	public ServiceProxy(T target) {
    
    
        this.target = target;
    }
}

Aplicación genérica

  1. Al operar una colección

La razón por la que se pueden almacenar varios tipos de objetos en el contenedor es por la introducción de genéricos

List lists = new ArrayList<String>();
  1. Cuando se usa en componentes básicos

Porque el requisito de los componentes es lograr un cierto grado de versatilidad y es necesario admitir diferentes tipos, y la característica de los genéricos es que los tipos específicos solo se aclaran al crear objetos o llamar a métodos. Puede referirse SpringData JPAa JpaRepositoryla redacción:

public interface JpaRepository<T, ID> extends 
  PagingAndSortingRepository<T, ID>, QueryExampleExecutor<T> {
    
    
  	List<T> findAll();
  	List<T> findAll(Sort sort);
  	List<T> findAllById(Iterable<ID> ids);
  	<S extends T> List<S> saveAll(Iterable<S> entities);
  	void flush();
   	<S extends T> S saveAndFlush(S entity);
   	void deleteInBatch(Iterable<T> entities);
   	void deleteAllInBatch();
   	T getOne(ID id);
  
   	@Override
   	<S extends T> List<S> findAll(Example<S> example);
  
   	@Override
   	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

Además, en el componente, también es inseparable de Java 反射机制, generalmente反射+泛型

Da otro ejemplo práctico

Por ejemplo, existe el requisito de agregar ciertos campos de ciertas tablas de la base de datos. La declaración SQL es

select sum(column1),sum(column2) from table group by field1,field2

Las necesidades sumy las group bycolumnas las pasa el propio negocio de la parte, mientras que la tabla SQL es en realidad nuestro campo de entrada POJO, POJO y ciertamente un atributo de una sola empresa puede escribir POJO murió en los parámetros, pero si el parámetro está configurado como genérico, puede mejorar la reutilización del código.

Después de obtener el parámetro, obtenga el valor específico de su campo a través de la reflexión, y puede hacer la acumulación. Los ejemplos de código específicos son los siguientes:

// 传入需要 group by 和 sum 的字段名
public cacheMap(List<String> groupByKeys, List<String> sumValues) {
    
    
  	this.groupByKeys = groupByKeys;
  	this.sumValues = sumValues;
}

private void excute(T e) {
    
    
  
    // 从pojo 取出需要group by 的字段 list
    List<Object> key = buildPrimaryKey(e);

    // primaryMap 是存储结果的Map
    T value = primaryMap.get(key);

    // 如果从存储结果找到有相应记录
    if (value != null) {
    
    
        for (String elem : sumValues) {
    
    
          // 反射获取对应的字段,做累加处理
            Field field = getDeclaredField(elem, e);
            if (field.get(e) instanceof Integer) {
    
    
                field.set(value, (Integer) field.get(e) + (Integer) field.get(value));
            } else if (field.get(e) instanceof Long) {
    
    
                field.set(value, (Long) field.get(e) + (Long) field.get(value));
            } else {
    
    
                throw new RuntimeException("类型异常,请处理异常");
            }
      	}
        // 处理时间记录
        Field field = getDeclaredField("updated", value);
        if (null != field) {
    
    
            field.set(value, DateTimeUtils.getCurrentTime());
        }
    } else {
    
    
      	// group by 字段 第一次进来
        try {
    
    
            primaryMap.put(key, Tclone(e));
            createdMap.put(key, DateTimeUtils.getCurrentTime());
        }catch (Exception ex) {
    
    
         	 	log.info("first put value error {}" , e);
        }
    }
}

Referencia: https://mp.weixin.qq.com/s/fL6cMjSNAX_fZ7wFBJO80Q

Supongo que te gusta

Origin blog.csdn.net/weixin_44471490/article/details/111658775
Recomendado
Clasificación