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. List
La 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 inciertoT
Representa un tipo específico de Java (Tipo)K
,V
Representar respectivamente el valor clave en el valor clave de JavaE
Representa elemento
Tres formas de usar genéricos
Los genéricos son generalmente de tres 泛型类
maneras: 泛型接口
, 泛型方法
,.
Clase genérica
Esto T
se 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();
}
- Implemente una interfaz genérica sin especificar el tipo:
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
- 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:
-
El código es más conciso, no es necesario forzar la conversión de tipo
-
El programa es más robusto, no hay advertencias durante la compilación ni
ClassCastException
excepciones durante el tiempo de ejecución. -
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
- 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>();
- 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 JPA
a JpaRepository
la 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 sum
y las group by
columnas 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