背景
最近这一份工作是做跨境电商的对接了Amazon、aliexpress、shopee、ebay等多个平台,发现每对接一个平台都是用复制大法,重复造一些轮子,为了提升工作效率,早点下班,封装了一些代码,使用泛型相对比较多,所以写这篇记录一下。
泛型是什么
记得以前面试的时候经常被问泛型是什么,为什么要使用泛型? 泛型:可以说是类型参数化。也就是说操作的数据类型,被指定为一个参数,这种参数可以用在类、接口、方法上,分别可以叫做泛型类、泛型接口、泛型方法。
代码语言:javascript复制public static void main(String[] args) {
List list = Lists.newArrayList();
list.add("String");
list.add(0);
for (int i = 0; i < list.size(); i ) {
String str = (String) list.get(0);
System.out.printf("str");
}
}
我们知道List可以放入任意类型,但是上面代码我们放入了字符串和整形,所以在运行的时候肯定会报错。
所以我们通常的做法在创建List的时候就指定了他的数据类型(我们这里可以理解数据类型被指定了,是上面说的类型参数化)
代码语言:javascript复制List<String> list = new ArrayList<>();
当我们指定了泛型,我们在编译的时候就发现错误,不用等运行的时候,这也是使用泛型的一个好处。
泛型类
代码语言:javascript复制class Test1<T> {
private T t;
public void test(T t) {
System.out.printf("ttttt");
}
public T test1() {
return t;
}
public T test2(T t) {
return t;
}
//报错
public void test3(E e){
}
//泛型方法
public <E> void test3(E e){
}
}
上面就是一个泛型类:泛型类是指在实体化对象的是就指定了操作的类型
代码语言:javascript复制new Test1<String>
泛型方法
上面泛型类中还有一个泛型方法:泛型方法是在调用的时候指定类型 上面来标记这个是一个泛型方法,跟后面的T是不一样的,比如下面这个也是一个泛型方法
代码语言:javascript复制//泛型方法
public <K,V> V test3(K k){
return (V)"1";
}
public static <T> List<List<T>> splitList(List<T> list, int size) {
int length = list.size();
int num = (length size - 1) / size;
List<List<T>> newList = new ArrayList<>(num);
IntStream.range(0, num).forEach(i -> {
newList.add(list.subList(i * size, Math.min((i 1) * size, length)));
});
return newList;
}
泛型接口
泛型接口的定义跟泛型类基本相同
代码语言:javascript复制//定义一个泛型接口
public interface Test<T> {
public T next();
}
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class TestClass<T> implements TestInter<T>{
* 如果不声明泛型,如:class TestClass implements TestInter<T>,编译器会报错:"Unknown class"
*/
class Test1<T> implements TestInter<T>{
@Override
public T next() {
return null;
}
}
如果是泛型实参
代码语言:javascript复制/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口TestInter<T>
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:TestInter<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class TestClass implements TestInter<String> {
@Override
public String next() {
return "";
}
}
泛型通配符
代码语言:javascript复制public void test(List<Number> obj){
}
我们知道Integer是Number的子类那我们调用test方法如下
代码语言:javascript复制List<Number> list = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
test(list) //对
test(list1) //报错
这个方法编译器会为我们报错:List<java.lang.Integer>
// cannot be applied to List<java.lang.Number>
通过提示信息我们可以看到List不能被看作为`List的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
代码语言:javascript复制我们总不能再定义一个方法
public void test(List<Integer> obj){
}
我们可以这样写
public void test(List<?> obj){
}
public void delete(List<? extends BasePublishProductMongo> list, Set<String> ids) {
if (CollectionUtils.isEmpty(list)) {
throw new PreviewException("删除失败,未查询到数据");
}
}
? 或者 ?extends xxx 可以当做类似String,Integer是个泛型实参,类型不确定时候使用。
泛型具体使用例子
代码语言:javascript复制import java.util.concurrent.Callable;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author dingd
* @Description Hystrix线程池没有传递threadlocal所以对callable进行一个包装
* @createTime 2021-07-21 15:08:34
*/
@Order(1000)
@Component
public class HystrixCallableOrderSyncLogWrapper implements HystrixCallableOrderedWrapper {
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new WrappedCallable<>(callable, LOG_THREAD_LOCAL.get());
}
private static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final OrderSyncLogCollector collector;
public WrappedCallable(Callable<T> target, OrderSyncLogCollector collector) {
this.target = target;
this.collector = collector;
}
public T call() throws Exception {
try {
if (collector != null) {
LOG_THREAD_LOCAL.set(collector);
}
return this.target.call();
} finally {
LOG_THREAD_LOCAL.remove();
}
}
}
}
参考:https://blog.csdn.net/s10461/article/details/53941091