Introduction aux génériques
Java generics ( generics
) est une nouvelle fonctionnalité introduite dans JDK 5. Les génériques fournissent un 编译时类型安全检测机制
mécanisme qui permet aux programmeurs de détecter les types illégaux au moment de la compilation.
L'essence des génériques est 参数化类型
que le type de données manipulées est spécifié comme un 参数
. Le type spécifique n'est spécifié que lorsque l'objet est créé ou que la méthode est appelée.
Effacement de type
La première condition préalable à une compréhension correcte des concepts génériques est la compréhension 类型擦除
.
Pour plus d'informations sur l'effacement de type, vous pouvez consulter cet article: «L'effacement de type générique Java et les problèmes causés par l'effacement de type»
Qu'est-ce que c'est
类型擦除
?
Le type générique de 伪泛型
Java est dû au fait que toutes les informations génériques seront effacées lors de la compilation Java. Les génériques de Java sont essentiellement implémentés au niveau du compilateur, et le bytecode généré ne contient pas les informations de type dans les génériques.
- Lorsque vous utilisez des génériques, ajoutez des paramètres de type et les paramètres seront supprimés lors de la compilation du compilateur. Ce processus est appelé
类型擦除
.
Par exemple List<String>
, les types définis dans le code deviendront après compilation. List
Les informations de type attachées par le type générique sont invisibles pour la JVM. La JVM ne voit que la liste. Le compilateur Java trouvera autant que possible les erreurs possibles au moment de la compilation, mais il est toujours incapable de générer des exceptions de conversion de type au moment de l'exécution. L'effacement de type est également 模板机制
une différence importante entre les génériques de Java et l' implémentation C ++ .
Caractère générique
Pour plus d'informations sur les jokers dans les génériques Java, vous pouvez consulter cet article: "Parlons des jokers T, E, K, V, dans les génériques Java ? 》
Les jokers couramment utilisés sont: T, E, K, V ,?
?
Représente un type Java incertainT
Représente un type Java spécifique (Type)K
,V
Représentent respectivement la valeur clé dans la valeur clé JavaE
Représente l'élément
Trois façons d'utiliser les génériques
Les médicaments génériques sont généralement trois 泛型类
façons: 泛型接口
, 泛型方法
,.
Classe générique
Cela T
peut être écrit comme n'importe quel identificateur. Des paramètres communs tels que T, E, K et V sont souvent utilisés pour représenter des génériques.
Lors de l'instanciation d'une classe générique, vous devez spécifier le type spécifique de T.
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
Instanciez la classe générique:
Generic<Integer> genericInteger = new Generic<Integer>(123456);
Interface générique
public interface Generator<T> {
public T method();
}
- Implémentez une interface générique sans spécifier le type:
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
- Implémentez une interface générique, spécifiez le type:
class GeneratorImpl<T> implements Generator<String>{
@Override
public String method() {
return "hello";
}
}
Méthode générique
public static <E> void printArray(E[] inputArray){
for(E element : inputArray){
System.out.printf( "%s ", element );
}
System.out.println();
}
utilisation:
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = {
1, 2, 3 };
String[] stringArray = {
"Hello", "World" };
printArray(intArray);
printArray(stringArray);
Avantages des génériques
Les avantages de l'utilisation de génériques sont:
-
Le code est plus concis, pas besoin de forcer la conversion de type
-
Le programme est plus robuste, il n'y a aucun avertissement lors de la compilation, et aucune
ClassCastException
exception lors de l'exécution -
Améliorez la réutilisabilité du code, par exemple: dans un proxy dynamique, les génériques peuvent proxy toute classe qui doit être proxy
public class ServiceProxy<T> implements InvocationHandler {
private T target;
public ServiceProxy(T target) {
this.target = target;
}
}
Application générique
- Lors de l'exploitation d'une collection
La raison pour laquelle divers types d'objets peuvent être stockés dans le conteneur est à cause de l'introduction de génériques
List lists = new ArrayList<String>();
- Lorsqu'il est utilisé dans les composants de base
Parce que l'exigence des composants est d'atteindre un certain degré de polyvalence et de prendre en charge différents types, et la caractéristique des génériques est que les types spécifiques ne sont clarifiés que lors de la création d'objets ou de l'appel de méthodes. Peut se référer SpringData JPA
au JpaRepository
libellé:
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);
}
De plus, dans le composant, il est également indissociable de Java 反射机制
, généralement反射+泛型
Donnez un autre exemple pratique
Par exemple, il est nécessaire d'agréger certains champs de certaines tables de base de données. L'instruction SQL est
select sum(column1),sum(column2) from table group by field1,field2
Les besoins sum
et les group by
colonnes sont transmis par la propre entreprise de la partie, tandis que la table SQL est en fait notre champ POJO, POJO entrant et attribue certainement une seule entreprise peut réellement écrire POJO mort sur les paramètres, mais si le paramètre est défini sur générique, vous pouvez améliorer la réutilisabilité du code.
Après avoir obtenu le paramètre, obtenez la valeur spécifique de son champ par réflexion, et vous pouvez faire l'accumulation. Des exemples de code spécifiques sont les suivants:
// 传入需要 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);
}
}
}
Référence: https://mp.weixin.qq.com/s/fL6cMjSNAX_fZ7wFBJO80Q