泛型的高级使用

2022-08-12 20:28:29 浏览数 (1)

背景

最近这一份工作是做跨境电商的对接了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

0 人点赞