java泛型总结
本文参考了 原文链接:https://blog.csdn.net/weixin_45395059/article/details/126006369,且部分内容转自 原文链接:https://blog.csdn.net/weixin_45395059/article/details/126006369,原文对泛型的理解非常透彻,大家可以去看原文,本文加了部分自己的理解
一泛型类
泛型类的定义
public class Generic<T> {
// key 这个成员变量的数据类型为 T, T 的类型由外部传入
private T key;
// 泛型构造方法形参 key 的类型也为 T,T 的类型由外部传入
public Generic(T key) {
this.key = key;
}
// 泛型方法 getKey 的返回值类型为 T,T 的类型由外部指定
public T getKey(){
return key;
}
}
泛型类中静态属性和方法不可使用泛型类定义的泛型
public class Test2<T> {
public static T one; // 编译错误,静态变量不可使用,类声明的泛型参数
public static T show(T one){ // 编译错误 静态方法不可使用,类声明的泛型参数
return null;
}
// 泛型类定义的类型参数 T 不能在静态方法中使用
public static <E> E show(E one){ // 这是正确的,因为 E 是在静态方法签名中新定义的类型参数
return null;
}
}
· 泛型接口
public interface Myinterface <T>{
// 1.接口中的所有方法默认都是 public abstract的
// 2.接口中的所有属性默认都是 public static fina
// 3.jdj之后允许在接口实现默认方法和静态方法
//报错,根据2,静态属性不可以使用类的泛型
T a;
//不报错
<U> void test1(T t,U u);
//报错,静态方法不可以使用接口的泛型
static void test2(T t){
System.out.println("ss");
}
//不报错
default void test3(T t){
System.out.println("ss");
}
}
泛型接口的实现类
//在实现接口的时候指定泛型,这样就会把泛型替换成我们指定的,否则将自动替换成object类型
public class MyinterfaceImpl implements Myinterface<Integer> {
@Override
public <U> void test1(Integer integer, U u) {
}
@Override
public void test3(Integer integer) {
Myinterface.super.test3(integer);
}
}
泛型的本质是将数据类型参数化,它通过擦除的方式来实现,即编译器会在编译期间擦除代码中的所有泛型语法并相应的做出一些类型转换动作。
换而言之,泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。也就是说,成功编译过后的 class 文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段。
二 上界通配符 <? extends T>
List list = new ArrayList(); 体现了多态特性,实现ArrayList 向list转型
那么ArrayList numberList =new ArrayList(),是否合法呢
答案是不合法
如果这种情况合法就会出现这样的情况
// “向上转型”为 ArrayList<Number> 这里是不被允许的,我们假设编译器不报错
ArrayList<Number> numberList =new ArrayList<Integer>();
// 添加一个 Float 对象,Float 也是 Number 的子类,编译器不报错
numberList.add(new Float(11.3));
//添加一个Integer对象
numberList.add(new Integer(11));
// 从 ArrayList<Integer> 集合中获取索引为 1 的元素(即添加的 Float 对象):
Integer n = integerList.get(1); // ClassCastException,运行出错
那么ArrayList numberList =new ArrayList(),该如何实现呢
使用上界通配符, ArrayList<?extend Number> numberList =new ArrayList();即可
这行代码将不会报错,<?extend Number>,的上界类型为Number,他可以指代Number的任务一个子类型包括Number,但是你不可以指定这个子类型,例如添加数据,
所以上界通配符是只读的,下面是正确用法,利用上界通配符接受参数,打印数据
public class Generic {
public static void main(String[] args) {
// List<Number> list1 =new ArrayList<Integer>();
List<Number> list=new ArrayList<Integer>();
//上界通配符检验
List<Integer> list2=new ArrayList<>();
list2.add(1);
List<Float> list3=new ArrayList<>();
list3.add(1.23f);
printValue(list2);
printValue(list3);
}
//上界通配符 <? extends T> 接受List<Integer> List<Float> ,
public static void printValue(List<? extends Number> list){
//不可向list中添加数据,因为为了避免上面的违法情况,添加类型相当于指定<? extends Number>的类型,这是不被允许的
//所以上界通配符常常用来读数据,写数据是非法的
for (Number number : list) {
System.out.println(number);
}
}
}
二 下界通配符 <? superT>
<? super Number>可以指代 Number以上的所有父类型,但是和下界通配符一样,你不可以为其指定类型,也就是不可以添加Number类的父类型数据,,
List<? super Number> list4=new ArrayList();
//此处将会报错,因为 <? extends Number>可以指代 不包括Integer以上的所有父类型,添加Integer以上的所有父类型数据,相当于指定了类型,报错,但是可以添加Number及其以下的所有子类型,不会报错
list4.add(new Object());
List<? super Number> list4=new ArrayList();//则可以向list4里面添加Number子类以下的数据
List<? super Number> list4=new ArrayList();
list4.add(new Integer(3));
list4.add(new Float(2.1));
List<? super Integer> list4= new ArrayList();这样也不会报错,传递的类型符合<? super Integer>表示的类型