Java 泛型(generics
)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制
,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型
,也就是说,所操作的数据类型被指定为一个参数
。在创建对象或调用方法的时候才明确具体的类型。
正确理解泛型概念的首要前提是理解类型擦除
。
更多关于类型擦除的问题,可以查看这篇文章:《Java泛型类型擦除以及类型擦除带来的问题》
什么是
类型擦除
?
Java的泛型是伪泛型
,这是因为Java在编译期间,所有的泛型信息都会被擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的。
类型擦除
。如在代码中定义List<String>
等类型,在编译后都会变成List
,由泛型附加的类型信息对JVM是看不到的JVM看到的只是List。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法在运行时刻出现的类型转换异常的情况,类型擦除也是Java的泛型与C++模板机制
实现方式之间的重要区别。
更多关于Java 泛型中的通配符可以查看这篇文章:《聊一聊-JAVA 泛型中的通配符 T,E,K,V,?》
常用的通配符为: T、E、K、V、?
?
表示不确定的 Java 类型T
表示具体的一个Java类型(Type)K
、V
分别代表 Java 键值中的 Key ValueE
代表Element泛型一般有三种使用方式:泛型类
、泛型接口
、泛型方法
。
泛型类
此处T
可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
在实例化泛型类时,必须指定T的具体类型。
public class Generic<T>{ private T key;public Generic(T key) { this.key = key;}public T getKey(){ return key;}
}
实例化泛型类:
Generic<Integer> genericInteger = new Generic<Integer>(123456);
泛型接口
public interface Generator<T> {public T method();
}
class GeneratorImpl<T> implements Generator<T>{@Overridepublic T method() {return null;}
}
class GeneratorImpl<T> implements Generator<String>{@Overridepublic String method() {return "hello";}
}
泛型方法
public static <E> void printArray(E[] inputArray){ for(E element : inputArray){ System.out.printf( "%s ", element );}System.out.println();
}
使用:
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray(intArray);
printArray(stringArray);
使用泛型的好处就是:
代码更加简洁,不再需要强制类型转换
程序更加健壮,在编译期间没有警告,在运行期就不会出现ClassCastException
异常
提高代码的复用性,比如:在动态代理中,通过泛型可以代理任何需要被代理的类
public class ServiceProxy<T> implements InvocationHandler {private T target;public ServiceProxy(T target) {this.target = target;}
}
容器中之所以可以存储各种类型的对象,就是因为引入了泛型
List lists = new ArrayList<String>();
因为组件的要求是需要做到一定的通用性,需要支持不同的类型,而泛型的特点是:在创建对象或调用方法的时候才明确具体的类型。可以参考SpringData JPA
的JpaRepository
的写法:
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);
}
此外,在组件中,还离不开Java的反射机制
,一般是反射+泛型
再举个实际的例子
比如有个需求是将某些数据库表的某些字段进行聚合,SQL语句就是
select sum(column1),sum(column2) from table group by field1,field2
需要sum
和group by
的列是由业务方自己传入,而SQL表其实就是我们的POJO,传入的字段肯定也是POJO的属性,单个业务实际可以在参数上将POJO写死,但是如果参数设置为泛型,便可以提高代码的复用性。
拿到参数后,通过反射获取其字段具体的值,就可以做累加了。具体代码示例如下:
// 传入需要 group by 和 sum 的字段名
public cacheMap(List<String> groupByKeys, List<String> sumValues) {upByKeys = groupByKeys;this.sumValues = sumValues;
}private void excute(T e) {// 从pojo 取出需要group by 的字段 listList<Object> key = buildPrimaryKey(e);// primaryMap 是存储结果的MapT value = (key);// 如果从存储结果找到有相应记录if (value != null) {for (String elem : sumValues) {// 反射获取对应的字段,做累加处理Field field = getDeclaredField(elem, e);if ((e) instanceof Integer) {field.set(value, (Integer) (e) + (Integer) (value));} else if ((e) instanceof Long) {field.set(value, (Long) (e) + (Long) (value));} else {throw new RuntimeException("类型异常,请处理异常");}}// 处理时间记录Field field = getDeclaredField("updated", value);if (null != field) {field.set(value, CurrentTime());}} else {// group by 字段 第一次进来try {primaryMap.put(key, Tclone(e));createdMap.put(key, CurrentTime());}catch (Exception ex) {log.info("first put value error {}" , e);}}
}
参考:
本文发布于:2024-01-28 07:35:13,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063985195828.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |