为什么要用泛型:
1、同一套代码能够兼容不同的类型
2、能够在编译前确定一个类能接收哪种类型,不至于运行时再确认
3、规避杂乱地使用Object变量,产生的强转异常
ArrayList的add方法即是一个典型案例:
public boolean add(E var1) {
this.ensureCapacityInternal(this.size + 1);
this.elementData[this.size++] = var1;
return true;
}
这种情况下,只需要创建ArrayList时候声明泛型类型,该方法就能被指定参数类型;否则ArrayList需要如下多个重载方法:
public boolean add(String var1);
public boolean add(Boolean var1);
public boolean add(Integer var1);
...
基本使用方法
ArrayList<String> arrayList = new ArrayList<>();
泛型声明如果不明确使用,则默认为Object类型 JDK7开始后面的类型声明可以省略.泛型声明类型只能被引用数据类型填充
1、 在类上声明泛型
类中使用到E类型的地方会被限定类型
public class NormalGeneric<T>{
private T data;
public NormalGeneric(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static void main(String[] args) {
NormalGeneric<String> normalGeneric = new NormalGeneric<>("Heria");
System.out.println(normalGeneric.getData());
}
}
2、 在方法中声明泛型
可以和类中声明的泛型不一致,方法调用时候,传入什么类型就当做什么类型处理
public class EObj<T> {
public <E> void show(E e){
System.out.println(e);
}
}
public static void main(String[] args) {
TestClass testClass = new TestClass();
TestClass.EObj eObj = testClass.new EObj<String>();
eObj.show("pdddd");
eObj.show(14);
eObj.show(true);
//这里还涉及到自动装箱和拆箱的机制
}
public class GenericFunction {
private <T> void showData(T t) {
System.out.println(t.toString());
}
private <K> K getOtherData(K... ks) {//真正意义上的泛型方法
return (ks[ks.length / 3]);
}
private <T, K> K getOtherData2(T... ts) {
return null;
}
public static void main(String[] args) {
GenericFunction genericFunction = new GenericFunction();
System.out.println(genericFunction.<String>getOtherData("2", "3", "4"));
System.out.println(genericFunction.getOtherData(new TheoryClass<>(), new TheoryClass<>(), new TheoryClass<>()));
}
}
3、在接口中声明泛型
public interface FanAdapter<E> {
void show(E e);
}
//实现方案一,将类型提前声明
private class FanAdapterImp implements FanAdapter<String>{
@Override
public void show(String s) {
}
}
//实现方案二,将泛型带到子类中
//集合框架常用该设计
private class FanAdapterImp<E> implements FanAdapter<E>{
@Override
public void show(E e) {
}
}
需要注意的点:
- 如果泛型类型在类上声明,不能在未指定具体类型时候就在构造方法中生成泛型类型对象
public class Restrict<T> {
private T mData;
public Restrict() {
//T t = new T(); // 不可用
}
}
- 泛型参数类型之间的关系不能决定泛型类之间的关系
Restrict<String> restrict1 = new Restrict<>();
Restrict<Double> restrict2 = new Restrict<>();
System.out.println(restrict1.getClass() == restrict2.getClass());
System.out.println(restrict1.getClass());
System.out.println(restrict2.getClass());
输出/*true
class com.example.fanxing.Restrict
class com.example.fanxing.Restrict*/
//泛型类型获取类型时候获取的是原生类型
TongPei<Fruit> tongPei = new TongPei<>();
TongPei<Orange> orangeTongPei = new TongPei<>();
//Fruit和Orange是父子类关系,但是tongPei = orangeTongPei会报错
- 泛型不能被捕获,但是能主动抛出
private <T extends Throwable> void function (T t) {
try{
}catch (T t){//不能被捕获
}
}
private <T extends Throwable> void function2 (T t) throws T {
try{
}catch (Throwable e){//可以被主动抛出
throw t;
}
}
- 静态声明方法中不能直接使用泛型类上声明的泛型类型,但如果把静态方法声明为一个泛型方法就没问题
public class Restrict<T> {
private T mData;
public static T function() { // 不可用,泛型类型在实例化对象时候才确定,静态方法类型确定在实例化之前
return null;
}
public static <E> void function2() {//ok
}
}
4、泛型中的通配符
既然泛型参数类型不能决定泛型类的继承关系,但实际开发中我们需要他们能够具备这样但关系,比如我们希望一个声明了参数类型为TongPei<Fruit>的方法,传入TongPei<Orange>也行得通,因为Orange是Fruit的子类,那么就需要通配符帮忙
- 通配符<?>,匹配任意类型
//指定的泛型中前后类型必须一致
Collection<String> collection = new ArrayList<String>();//correct
Collection<String> collection1 = new ArrayList<Object>();//wrong
ArrayList<String> arrayList = new ArrayList<Object>();//wrong
//?通配符下的泛型类型可以引用任何类型
Collection<?> collection3 = new ArrayList<Boolean>();
Collection<?> collection4 = new ArrayList<Integer>();
ArrayList<?> collection2 = new ArrayList<String>();
- 向下限定 可接受E和E的子类
ArrayList<? extends Animal> arrayList = new ArrayList<Animal>();
ArrayList<? extends Animal> arrayList1 = new ArrayList<Cat>();
ArrayList<? extends Animal> arrayList2 = new ArrayList<Dog>();
ArrayList<? extends Animal> arrayList3 = new ArrayList<String>();//wrong
- <* super E> 向上限定 可接受E和E的父类
ArrayList<? super Animal> arrayList1 = new ArrayList<Object>();
ArrayList<? super Animal> arrayList2 = new ArrayList<Animal>();
ArrayList<? super Animal> arrayList3 = new ArrayList<Cat>();//wrong
需要注意的点:
/ 限定泛型的继承类型 规则同Java继承规范,即只能继承一个类同时可以继承多个接口
public class Extends<T extends ArrayList&Comparable&Runnable> {
}
class Exrtends2 {
private <E extends ArrayList&Comparable&Runnable> void extrendsFun() {
}
}
5、泛型实现原理
//JDK实现泛型的原理是类型擦除
//下面的例子中会把T统一修改成Object
public class TheoryClass<T> {
private T mData;
public T getmData() {
return mData;
}
public void setmData(T mData) {
this.mData = mData;
}
public static void main(String[] args) {
TheoryClass<String> theoryClass = new TheoryClass<>();
TheoryClass<Double> theoryClass1 = new TheoryClass<>();
}
}
//这种情况下泛型会被统一擦除成Arraylist,如果都是接口会被统一擦除成实现的第一个接口
//如果在泛型的第某个方法中用到了Comparable接口,那么泛型参数前会被添加一个强制转型声明
class TheoryClass2<T extends ArrayList&Comparable> {
}