何为泛型
正如字面意思,泛化的类型,指在编码时无法确定某一个具体的类型,需要先使用一个占位符(建议大写,全英),当运行时传入具体的类型来替换这一个泛型标记
为什么需要泛型
伪需求
假设我们需要一个列表去存 String
类型的数据,那这个结构的设计为
class MyListForString{
String get();
void set(Sring int)
}
然后,发现需要里一个列表去存取 Integer
类型的数据,就需要重新定义一个结构
class MyListForInteger{
Integer get();
void set(Integer int)
}
然后,我们需要一个列表存储 xx 类型的数据。。
用 Object 优化
这个需求抽出来,其实就是需要有一个列表结构,然后能够存储一个指定的类型(这个类型又可能会根据需要进行变化),并且需要能正确的取出对应的类型,现在,我们为了应对变化的需求,重复编写差不多的 MyListForXX
根据多态以及向上转型的知识点(不懂的 xdm 可以来看看我准备的前置知识点 Java 三大基础特性の多态),我们可以用 Object
优化为成下面的代码块
class MyList{
Object get();
void set(Object obj)
}
现在已经能做到通用性了,就是用起来,emm,得小心一点
MyList list = new MyList();
list.set(1);
list.set("1");
Integer a = (Integer)list.get(); // 代码中取出来的类型实际是 Object, 我们需要手动强制为存入时的类型
String s = (String)list.get();
正如伪代码演示的这样,我们存入数据的时候,没有限制,但是,取出来的话,就需要小心翼翼了,毕竟谁也不知道 list
中存入了什么类型的数据,或许,我们可以修改实例创建的描述来实现一点点毫无约束力的限制?
MyList listOnlyForInt = new MyList(); // 注意看实例名
list.set(1);
泛型优化
或许是受够了这种软弱无力的约束,java 在 JDK5
中终于引入了泛型
用泛型的思路来对上面的代码进行修改,好处是显而易见的,我们在创建 MyList
的实例时,就告诉了 JVM 这个实例操作的泛型 T 的实例类型是 String
,JVM 会在我们存入数据的数据校验数据类型是否匹配,会在我们取出的时候,自动的强转为对应的类型
JVM 的内部并没有什么魔法,底层的使用的类型还是 Object,但是,JVM 会根据我们传递的具体泛型类型来做入参时的校验,出参时的强转
class MyList<T>{
T get();
void set(T int)
}
MyList<String> strs = new MyList<String>();
String s = strs.get();
java.util.ArrayList
来阅读一下我们经常使用的集合类 ArrayList
的 get(), add()
的源码
1、集合里实际上使用了 Object[]
的数组
2、add()
将数据存入到了 object
的数组当中,这里涉及到一个向上转型的隐藏操作(变相的验证,所有的 class
都是继承至 Object
)
3、get()
对获取到的数据,强转为泛型 E
的实际类型
public E get(int index) { // line: 432
rangeCheck(index);
return elementData(index);
}
E elementData(int index) { // line: 421
return (E) elementData[index];
}
public boolean add(E e) { // line: 461
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
transient Object[] elementData; // non-private to simplify nested class access // line: 135
原来这就是泛型
从上面例子来看,细心的同学应该发现了,“咦,原来我写 List, Set, Map
那些集合的操作就是用上了泛型鸭?”
随手写几个:
List<String> list = new ArrayList<>();
Set<Integer> set = new HashSet<>();
HashMap<String, User> hashMap = new HashMap<>();
没错,泛型的基础使用就那么回事
泛型的边界
在泛型的世界里,其实也有类似继承关系的说法,比如 <T extends String>, <T super String>
extends
class MyList<T extends String>{
T get();
}
这段代码看起来没有多大的改变,唯一的区别就是泛型 T 是继承自 String
的某一个类型
此外,因为是继承关系,我们可以获取到任意类型的泛型实例 T,并将他们向上转型并当成 String
处理,然后通过多态的思路去实际调用泛型 T 中对应的方法
super
class MyList<T super String>{
void set(T int)
}
这里可以理解为,我们运行时传入的泛型 T
,是 String
的父类,具体是哪一级的父类就不得而知
extends 与 super 的 区别
super
和 extends
不同的一点就是, 使用了 <T extends XXX>
的泛型操作可以 取值 当成 XXX 处理,使用了 <T super XXX>
的泛型只能将 XXX 对应的实例 传入 给泛型类型处理
类型擦除
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
Class c3 = new ArrayList().getClass();
System.out.println(c1 == c2); // true
System.out.println(c1 == c3); // true
从上述的代码来看,ArrayList
虽然申明了具体的泛型,或者是不带泛型,他们对应的 Class
全是一样的?变相的说明,在 class
中,根本就不存在泛型的痕迹?
方法上的泛型
虽然整篇文章都在讲 class
上的泛型,实际上,泛型也是能应用与方法上的
但是泛型是没有办法直接初始化的,需要我们传入一个具体的泛型实例,或者是对应的泛型 class
泛型方法的定义格式为 [作用域] [static] <T> T 方法名(参数列表)
以我封装的一个 JsonUtil.toObject()
为例,方法的定义就是 public static <T> T toObject()
public static <T> T toObject(String json, Class<T> clz) {
if (json == null) {
try {
return clz.newInstance();
} catch (Exception e) {
LogUtil.exception("JsonUtil.toObject: <T>", e);
}
}
return json_.toObject(json, clz);
}
public static <T> T toObject(String json, T t) { // 或者我们也可以传入泛型的一个具体的实例,不过,这种情况会非常的少见
toObject(json, t.getClass());
}
// 伪代码调用
User user = JsonUtil.toObject(jsonStr, User.class);
泛型的标记
在本篇文章中,大量使用了标记 T,来表示泛型,我们也可以换成其他的符号来标记,但都要求,先使用 <标记>
的方式声明这是一个泛型操作,比如演示代码
class List<E>{}
<A> A get();
<A, B, C> Tuple<A,B,C> tupleInstance(){}
元组
忘记介绍一个应用了泛型还比较好玩的结构,元组,这个数据结构的意义是 为方法返回多个参数,当然,我们用 map, list 也能实现,但不如元组这么优雅
首先,我们定义一个返回 2 个泛型类型的 元组 结构
class Tuple<A, B>{
final A a;
final B b;
public Tuple(A a1, B b1){
a = a1;
b = b1;
}
}
就和初始化一个普通的 class 一样,用法也没什么差异,变化的地方就是这个元组内部的2个字段,他们的 class
是在运行时确定下来了,我们可以在需要的时候,任意变化他们的类型
Tuple<Integer, String> tuple = new Tuple<>(1, "1");
Integer a = tuple.a;
String b = tuple.b;
java 里的泛型特性就先介绍这么多,hxd 给我的点赞和收藏是最大的鼓励
作者:安逸的咸鱼
链接:https://juejin.cn/post/6999098482665619464
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。