泛型
文章目录
1 泛型基础
理解:泛型就是假定一个类型,然后先用再说。
声明一个T,那么这个T就直接可以用,具体指哪个类型需要实例化时赋值。
1.1 三种泛型应用
泛型包含泛型类,泛型接口,泛型方法
1.1.1 泛型类
/**
* <值接受泛型的树节点类>
*/
public class OspTreeNode<T> {
//节点值
private T value;
//左子节点
public OspTreeNode<T> left;
//右子节点
public OspTreeNode<T> right;
/*
* 注意这里是泛型类中的普通方法,不是泛型方法
* 这里的T只是使用的类声明的泛型
*/
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
public static void print() {
System.out.println(this.value);
}
}
- 类中声明的泛型不能在静态方法中使用
因为泛型指代的是任意一个具体的类型,需要实例化时进行传类型值,静态方法类加载时加载到类中,加载时无法获取T所实例的具体类型。
- 注意区分【泛型方法】和使用了类声明的泛型的普通方法
1.1.2 泛型接口
示例:
OspTreeApi<T> 接口
/**
* <节点接受泛型类型的树接口>
*/
public interface OspTreeApi<T> {
/*
* <获取所有节点>
* 注意这里是泛型接口中的普通方法,不是泛型方法
* T只是使用了接口声明的泛型
*/
public List<T> getAllNodes();
}
实现方式1: 普通实现泛型接口,可以直接使用泛型接口中声明的泛型,可以和OspTreeApi定义的T不用同一符号。
OspBinaryTree<E>类
/**
* <节点接受泛型类型的二叉树>
*/
public class OspBinaryTree<E> implements OspTreeApi<E>{
/*
* <根节点>
*/
public OspTreeNode<E> root;
@Override
public List<E> getAllNodes() {
//TODO 中序遍历并放入List中返回
};
}
实现方式2: 实现声明了具体类型的泛型接口,一个泛型接口可以当作多个不同具体类型接口使用。
OspBinaryIntegerTree类
/**
* <节点接受Integer类型的二叉树>
*/
public class OspBinaryIntegerTree implements OspTreeApi<Integer>{
/*
* <根节点>
*/
public OspTreeNode<Integer> root;
@Override
public List<Integer> getAllNodes() {
//TODO 中序遍历并放入List中返回
};
}
1.1.3 泛型方法
-
需要在返回值类型前添加泛型标志用作声明,比如<A>或多个<A, B, C>
-
泛型方法声明的泛型是独立于所在类声明的泛型和其他方法声明的泛型的
-
静态方法使用泛型需要使用静态方法
public class Osp<T> {
// 泛型方法,声明泛型A
public <A> void osp0(A value) {
System.out.println(value);
}
// 静态泛型方法,声明泛型A,都是独立的,和其他泛型方法的泛型A不需保持一致
public static <A> void osp1(A value) {
System.out.println(value);
}
public <K, T> Map<K, T> osp2(K key, List<T> list) {
Map<K, T> map = new HashMap<>();
map.put(key, list.get(0));
return map;
}
public <K, V> Map<K, V> osp3(K key, List<T> list) {
Map<K, V> map = new HashMap<>();
map.put(key, (V)list);
return map;
}
}
1.2 泛型使用特点
- 泛型声明的对象无法使用instanceof (由于泛型擦除)
public void osp() {
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
//编译爆红: Illegal generic type for instanceof
if (stringRobList instanceof RobList<String>){}
}
- 泛型声明的对象获取类进行比较,结果为true,(由于泛型擦除)
不存在List<String>.class 或 List<Integer>.class,只有List.class
public void osp() {
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
// true
if (stringRobList.getClass().equals(integerRobList.getClass())) {};
}
### 1.3 泛型符号
任何一个大写字母都可指定泛型,通常使用T
常见的E,K,V等本质没有区别,只是约定"最好"用来表示某种类型
K V 表示key, value, 通常用于声明键值对中的键值,Map<K, V>
E 表示element,通常声明容器数据结构里的元素, List<E>
实际使用中都可以使用T或其他任意大写字母表示。
1.3.1 通配符?
?表示不确定类型
常用作实参传入方法
//任意类型元素的list都可以使用此方法
public void print(List<?> list) {
//TODO 遍历输出
}
?和普通泛型T等的区别:
-
T可用于声明泛型,并且需要声明(比如泛型类,泛型方法等先声明一个T,之后类中的方法才能使用T),?无法用来声明泛型,可以直接使用无需声明
-
T指代一个任意具体类型,?表示不确定类型
示例:
public class Osp<T> {
public void osp1(List<E> list) {
// 这里会爆红,E未被声明
}
public void osp2(List<?> list) {
// 可正常接受元素为任意类型的List
}
public void osp3(List<T> list) {
// 这里也不报错,因为是在类上声明过的T,指代具体的一个类型
//比如实例化Osp<Integer>,则osp3接受List<Integer>参数
}
}
1.4 定义多个泛型
尖括号内逗号隔开
public class Osp<K, V, A, B, C> {}
2 泛型擦除
- 定义
泛型擦除->编译器在编译后会把泛型具体类型去除,只留承载类编为字节码
- 擦除方式
编译时,字节码文件中去除对应的泛型类型声明,然后将类型引用处使用类上界取代,若未定义上界则用Object。
- 原因
-
兼容JDK1.5及之前的无泛型概念的版本
-
不同泛型类型都生成不同的字节码会造成代码膨胀,一份字节码对应一个对象占用内存
- 结果验证
public static void osp(RobList<?> list) {
// 泛型擦除,字节码存储基本类型
RobList<String> stringRobList = new RobList<>();
RobList<Integer> integerRobList = new RobList<>();
//stringRobList.getClass().equals(integerRobList.getClass()) -> true
// 泛型不能使用 instanceof
}
3 泛型的上下界
除了普通泛型,看经常看到在声明或在使用通配符时的<T extends Integer> <? super Integer> <? extends String>
-
<T extends Integer> 表示规定T泛型的上界,在声明了此泛型后,它接受Integer及其子类。
-
具体的类型T只能定义上界,?通配符可以定义上界和下界
-
<? extends String> 表示任意String及其子类(包含String自身)。
-
<? super Integer> 表示任意String及其父类(包含String自身)。
4 泛型是不协变的
协变指的是元素的继承关系会影响承载容器。
比如数组是协变的,String是Object子类,可以替换语句中Object的引用(里氏替换原则),String[]也可替换Object[]
泛型不协变,List<String>无法替换List<Object>
//数组是协变的
public Object[] getArr() {
String[] arr = new String[]{"1","2"};
return arr;
}
//泛型是不协变的
public LinkedList<Object> getList() {
LinkedList<String> list = new LinkedList<>();
// 编译爆红,类型不匹配
return list;
}
6万+

被折叠的 条评论
为什么被折叠?



