泛型方法

所谓泛型方法,就是带有类型参数的方法,它既可以定义在泛型类中(例如public void show(T aa),在使用泛型上没有任何特殊语法要求),也可以定义在普通类中(需要自定义参数类型,那么把泛型参数放在方法上就可以了,就是放在返回值类型之前,例如public <T> void show(T aa)),静态方法不能访问类的泛型,如果需要泛型只能在方法上使用泛型,例如

public static <T> void show(T aa)

具体应用案例

有一个接口IA,但是具体实现类不确定,具体实现类的创建交给配置文件

定义类或者方法时,不能确定所需要的参数类型

标准语法

依托于泛型类的写法

public class MyClass<T>{
    public void pp(T id){}
    public T abc(String name){}
    //不能是静态方法,如果静态则必须声明,而且和类上的T无关
 }

这种写法如果类已经确定则T的类型全部确定

不依靠泛型类,类有可能是没有泛型类

public class A{
    public <T> 返回类型 方法名称(T t){}  
    如果T有约束则必须使用<T extends IA>,方法可以是静态的,也可以是非静态
 }

特殊的通配符写法

public void pp(类型<? 约束> 参数名){}

约束可以使用extends和super两种

泛型类的继承

泛型类也是可以继承的,任何一个泛型类都可以作为父类或子类。不过泛型类与非泛型类在继承时
的主要区别在于:

  • 泛型类的子类必须将泛型父类所需要的类型参数,沿着继承链向上传递。这与构造方法参数必须
  • 沿着继承链向上传递的方式类似。
  • 子类不是泛型类:需要给父类传递类型常量,如class AA extends A<String>,如果不设置则默认class Aa extends A<Object>
  • 子类是泛型类:可以给父类传递类型常量,也可以传递类型变量。如class AA3<E> extends A<E>

如果父类中泛型有约束

public class A1<T extends Serializable> {}
public class B1<D extends Serializable> extends A1<D>{}  //声明子类型时在子类上声明约束
可以写成public class B1<D extends Serializable> extends A1{},此时A1中T就是Object类型的

泛型的擦除

实际上,从虚拟机的角度看,不存在泛型概念。

泛型是运用在编译时期的技术:编译时编译器会按照<类型名>的类型对容器中的元素进行检查,检查不匹配,就编译失败。如果全部检查成功,则编译通过,但编译通过后产生的.class文件中还有<类型名>这个标识,即字节码的类文件中有泛型,但是运行时并没有泛型这就是泛型的擦除。

可以通过使用javap反编译查看字节码文件,可以看到其中包含泛型

一句话总结就是:在.java文件运用泛型技术时,编译器在文件编译通过后运行时自动擦除泛型标识。

abstract class A1<TF> {
      private Class<TF> clz; // 就是T的具体类型
// 在构造器中获取T的具体类型
			public A1() {
				ParameterizedType c = (ParameterizedType) this.getClass().getGenericSuperclass(); // 获取到父类型的定义com.yan6.A1<T>
				clz=(Class)(c.getActualTypeArguments()[0]);  //获取真实的类型参数  T
			}

由于泛型的擦除,运行时并没有泛型机制,同时也没有使用向下类型转换,那么为何运行时无异常?

这是由于泛型的补偿

List<String> list=new ArrayList<>();
for(String tmp:list) System.out.println(tmp.length());  //泛型的补偿编
译器在擦除泛型后,会自动将类型转换为原定义的泛型,这样就不必再做向下类型转换了。
for(Object tmp:list){
 		if(tmp instanceof String){
 			System.out.println(((String)tmp).length());
 		}
 }

泛型的擦除和补偿这两个机制都是编译器内部自动完成的。

可以通过反射获取类型参数

泛型的局限性

  • 不能使用基本类型
  • 不能使用泛型类异常
  • 不能使用泛型数组
  • 不能实例化参数类型对象。例如T ob = new T();