Java generics (including applications of generics + reflection)

Introduction to Generics

Java generics ( generics) is a new feature introduced in JDK 5. Generics provide a 编译时类型安全检测机制mechanism that allows programmers to detect illegal types at compile time.

The essence of generics is 参数化类型, that is, the type of data being manipulated is specified as one 参数. The specific type is only specified when the object is created or the method is called.

Type erasure

The first prerequisite for a correct understanding of generic concepts is understanding 类型擦除.

For more information about type erasure, you can check this article: "Java Generic Type Erasure and the Problems Caused by Type Erasure"

What is it 类型擦除?

The generic type of 伪泛型Java is because all generic information will be erased during Java compilation. Java's generics are basically implemented at the level of the compiler, and the generated bytecode does not contain the type information in the generics.

  • When using generics, add type parameters, and the parameters will be removed when the compiler compiles. This process is called 类型擦除.

For example List<String>, the types defined in the code will become after compilation. ListThe type information attached by the generic type is invisible to the JVM. The JVM only sees the List. The Java compiler will find possible errors as much as possible at compile time, but it is still unable to generate type conversion exceptions at runtime. Type erasure is also 模板机制an important difference between Java's generics and C++ implementation.

Wildcard

For more information about wildcards in Java generics, please check this article: "Talk about the wildcards T, E, K, V, in Java generics ?

Commonly used wildcards are: T, E, K, V,?

  • ? Represents an uncertain Java type
  • T Represents a specific Java type (Type)
  • K, VRespectively represent the Key Value in the Java key value
  • E Represents Element

Three ways to use generics

Generics are generally three 泛型类ways: 泛型接口, 泛型方法, .

Generic class

This Tcan be written as any identifier. Common parameters such as T, E, K, and V are often used to represent generics.

When instantiating a generic class, you must specify the specific type of T.

public class Generic<T>{
    
     

    private T key;

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

    public T getKey(){
    
     
        return key;
    }
}

Instantiate the generic class:

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

Generic interface

public interface Generator<T> {
    
    
    public T method();
}
  1. Implement a generic interface without specifying the type:
class GeneratorImpl<T> implements Generator<T>{
    
    
    @Override
    public T method() {
    
    
        return null;
    }
}
  1. Implement a generic interface, specify the type:
class GeneratorImpl<T> implements Generator<String>{
    
    
    @Override
    public String method() {
    
    
        return "hello";
    }
}

Generic method

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

use:

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

Advantages of generics

The advantages of using generics are:

  1. The code is more concise, no need to force type conversion

  2. The program is more robust, there are no warnings during compilation, and no ClassCastExceptionexceptions during runtime

  3. Improve code reusability, such as: in dynamic proxy, through generics can proxy any class that needs to be proxy

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

Generic application

  1. When operating a collection

The reason why various types of objects can be stored in the container is because of the introduction of generics

List lists = new ArrayList<String>();
  1. When used in basic components

Because the requirement of components is to achieve a certain degree of versatility and need to support different types, and the feature of generics is that the specific types are only clarified when creating objects or calling methods. Can refer SpringData JPAto JpaRepositorythe wording:

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

In addition, in the component, it is also inseparable from Java 反射机制, generally反射+泛型

Give another practical example

For example, there is a requirement to aggregate certain fields of certain database tables, and the SQL statement is

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

Needs sumand group bycolumns are passed by the party's own business, while the SQL table is actually our POJO, POJO incoming field and certainly attribute a single business can actually write POJO died on the parameters, but if the parameter is set to generic , You can improve the reusability of the code.

After getting the parameter, get the specific value of its field through reflection, and you can do the accumulation. Specific code examples are as follows:

// 传入需要 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);
        }
    }
}

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

Guess you like

Origin blog.csdn.net/weixin_44471490/article/details/111658775