1、什么是泛型
泛型就是 参数类型化 ,简单说就是在定义类 接口 方法时时不支持具体的参数类型的。只有在实际使用时才确定。
2、为何用泛型
我觉得有两点好处
a. 明确类型 避免类型转换
如,没有泛型前,从List中取数据都是Object类型的 需要强制转换为确定的类型
public static void main(String[] args) {
List myList = new ArrayList();
myList.add(new Integer(1));
Integer value = (Integer)(myList.get(0));
System.out.println("value=" + value);
}
b. 提高代码复用
如有Father类 每个父亲可能有不同的职业 有点的教师 有点的医生...
如何表示呢,搞多个类表示不同的职业? 可以 但不友好!
使用泛型 如下:把职业类型化
使用:
Father<Teacher> fatherTeacher = new Father<>(new Teacher());
Father<Doctor> fatherDoctor = new Father<>(new Doctor());
public class Father<T> {
private T job;
public Father(T job){
this.job = job;
}
public void work(){
System.out.println("my job is "+ job);
}
}
3、如何使用泛型
泛型可以使用在类(泛型类)、 接口(泛型接口、 方法(泛型方法)中。
泛型类: 如上述的例子
泛型接口:
样例
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public interface Work<T> { public void work(T t); } 继承泛型接口 public class Father<T> implements Work<T>{ private T job; public Father(T job){ this.job = job; } @Override public void work(T t) { System.out.println("my job is "+ t); } }
泛型方法:方法的参数调用时才确定
例如下 :在泛型类中定义了一个泛型方法 (E是一种类型,跟类中的T 可能一样可能不一样)
泛型方法必须使用<> 表示类型
public class Father<T> implements Work<T>{
private T job;
public Father(T job){
this.job = job;
}
@Override
public void work(T t) {
System.out.println("my job is "+ t);
}
public <E> void printWork(E e){
System.out.println("my job is "+ e);
}
}
调用:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public static void main(String[] args) { Father<Teacher> fatherTeacher = new Father<>(new Teacher()); fatherTeacher.printWork(new Teacher()); }
输出:my job is com.yangfei.test.fanxing.Teacher@13221655
4、泛型擦除
泛型只在编译时有效 运行时是没有泛型的,称之为泛型擦除。
如下的示例 输出结果为true (java中一个类只会加载一次 生成Class类型对象)
说明两个类是没有泛型的区别的(编译后都被转成了Object)。
Class list_int = new ArrayList<Integer>().getClass();
Class list_string = new ArrayList<String>().getClass();
System.out.println(list_int==list_string);
泛型擦除的目的: 就是为了运行时不会创建过多的类 避免因泛型的区别导致加载的类泛滥。
泛型擦除非真正擦除: 否则运行时如何转换成实际的类型呢
如
List<String> sd = new ArrayList<String>();
sd.add("yangfei");
String str = list.get(0)
但取值的时候怎么知道要转成String类型的呢?
擦除并非真正的完全擦除,泛型信息还是会保存在class字节码的常量池中,
在使用了泛型的代码调用处会生成一个signature签名字段,signature指明了这个常量在常量池的地址,调用时强制转换
这样我们就找到了参数化类型。
参考: https://www.jianshu.com/p/2ded3ffaa9c8
5、协变
数组是协变的 什么意思
如果class A extends B 那么B[] array = new A[] 也是成立的,这是协变。
而泛型是不支持协变的
即 List<A>是不可以赋给List<B>的
6、 ?和T的区别
T表示一个类型 在使用时确定。
?时通配符 表示任意类型。
使用场景不一样
a. 在泛型类定义是使用T
b.在使用泛型时 可用?
如:
Father<?> fatherTeacher = new Father<Teacher>(new Teacher());
有啥好处,我们知道泛型不支持协变的,有了通配符后,可以有如下表示
父子类:class Son extends Mather; 则如下写法也是合法的
List<? extends Mather> lists = new ArrayList<Son>();
其实List<?> 相当于List<? extends Object>
7、PECS原则
PECS原则的全拼 : "Producer Extends Consumer Super"
表示<? extends T> 一般用于生产者,生产数据。外部只从中读数据;< ? super T> 一般用于消费者,需要消费数据,外部需要往其中写数据。
7.1 <? extends T> 表示可能是T或者T的子类 (上边界通配符)
这种情况一般只能取 不能存非null之外的任何类型
如下class Son extends Mather; class Daughter extends Mather;
public static void main(String[] args) {
Mather[] matherArr = new Mather[]{new Daughter(),new Son()};
List<? extends Mather> lists = Arrays.asList(matherArr);
lists.add(null); //因为任何类型都可以用null表示
lists.add(new Daughter()); //此处会编译报错
Mather mather = lists.get(0);
}
存非null值会报错 ,因为不知道具体是什么类型,Mather的子类和子类间并没有直接关系 没法赋值,所以没法存
取出来的值 只能是父类型(所有子类都可以转为父类)
7.2 <? super T> 表示可能是T或者T的父类 (下边界通配符)
这种情况一般只用于存数据 ,可以存T或T的子类,因为T的子类可以转换为T类型,而T类型也可以自动向上转为任何父类
取出的数据向上转换只能是object类型
public static void main(String[] args) {
Mather[] matherArr = new Mather[]{new Daughter(),new Son()};
List<? super Mather> lists = Arrays.asList(matherArr);
lists.add(null);
lists.add(new Daughter());
Object mather = (Object)lists.get(0);
}
8、不支持泛型数组
Java是不支持泛型数组的,如下编译会报错
List<String>[] array = new ArrayList<String>[5];
为何不支持:数组支持协变,泛型运行时类型擦除,这样容易引起误解
如下: List<Integer>类型数组 因为类型参数,可以把List<String>也放入数组中,导致内容错误。
ArrayList<Integer>[] intArr = new ArrayList<Integer>[10];
Object[] obj = intArr;
ArrayList<String> listStr = new ArrayList<String>();
listStr.add("yangfei")
obj[0] = listStr;
ArrayList<Integer> listInt = intArr[0];
Integer i = listInt.get(0);//想要获取Integer,但却是String
参考: https://www.cnblogs.com/hapjin/p/5371950.html
9、静态方法与类的泛型
一个泛型类只有在初始化时才能明确其类型 ; 而静态方法不属于实例。所以静态方法不能使用类的泛型
如下:标红会编译报错
public class Father<T> implements Work<T>{
private T job;
public Father(T job){
this.job = job;
}
@Override
public void work(T t) {
System.out.println("my job is "+ t);
}
public static void printWork(T e){
System.out.println("my job is "+ e);
}
}
解决方法:可使用泛型方法