背景:
类型转换中的ClassCastException,JDK5引入,JDK7后<>中的类型可以省略
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。
也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
好处:
编译时类型检查保证类型安全,消除强制类型转换,代码复用
定义泛型类
/** * 泛型类的定义语法: * class ClassName <泛型标识1,泛型标识2,泛型标识3...>{ * private 泛型标识 变量名 * }*/ public class Generic<T>{ /** * 常用的泛型标识有T,E,K,V */ private T key; public Generic(T key) { this.key = key; } public T getKey() { return key; } @Override public String toString() { return "Generic{" + "key=" + key + '}'; } }
使用泛型类
/** * 使用语法: * 类名<具体数据类型> 对象名 = new 类名<>(); * 泛型是在创建对象的时候,来指定操作的具体类型 * 不支持基本数据类型,只支持引用类型 * 如果不指定具体的数据类型,默认是Object类型 * 不同类型创建的泛型对象,都是相同的类型,与泛型类型无关,也就是getClass()结果相同 * */ Generic<String> strGeneric = new Generic<>("a"); Generic<Integer> intGeneric = new Generic<>(1); System.out.println(strGeneric.getClass()==intGeneric.getClass()); ---true
泛型类的子类
子类如果也是泛型类,子类和父类泛型类型要一致;因为子类加载前要先加载父类,如果不一致,父类不清楚是什么类型,会直接报错,无法加载
子类如果也是泛型类,至少要有一个和父类泛型类型一致,子类还可以有多个泛型类型
子类如果不是泛型类,父类要指定具体的泛型类型;
public class Parent<E> { private E value; public E getValue() { return value; } } /** * 父类不指定类型,则默认泛型类型是Object */ public class Child1<T> extends Parent{ /** * 返回的是Object */ @Override public Object getValue() { return super.getValue(); } } /** * 子类是泛型,那么子类和父类的泛型类型必须一致 * 子类可以有多个泛型类型 */ public class Child1<E,T,K> extends Parent<E>{ @Override public E getValue() { return super.getValue(); } } /** * 子类不是泛型类,父类必须明确数据类型 * 因为加载子类的时候需要先加载父类,父类不知道是什么类型,加载报错 */ public class Child2 extends Parent<E>{ --------报错 @Override public E getValue() { return super.getValue(); } }
泛型接口
和泛型类相似,定义语法:
interface 接口名称<泛型标识1,泛型标识2,泛型标识3>{ 泛型标识 方法名(); }
实现类如果是泛型类,实现类和接口的泛型类型要一致
实现类如果不是泛型类型,接口要明确泛型类型
例子和泛型类的差不多
泛型方法
语法:
修饰符 <T,K,E...> 返回类型 方法名(参数列表){
方法体
}
找到一张很好的图:
以<T>为例
修饰符和返回类型之间的<T>声明此方法是泛型方法,只有声明了<T>的方法才是泛型方法
泛型类中使用了泛型类型的方法不是泛型方法(因为这个泛型类型其实只是返回类型)
<T>表明该方法将使用泛型类型T,此时才可以在泛型方法中使用类型T
/** * 泛型方法 * @param list 参数 * @param idx 参数 * @param <E> 泛型方法的标识,具体类型由调用方法的时候来指定 * @return */ public <E> E getProduct(ArrayList<E> list,int idx){ return list.get(idx); }
类型通配符
类型通配符就是能代表任意类型的一个符号,比如 ?
@Data public class Box<E>{ private E ball; } public class Test { public static void main(String[] args) { Box<Number> box1 = new Box<>(); box1.setBall(100); showBox(box1); //Integer继承的Number,也不能直接调用 Box<Integer> box2 = new Box<>(); box2.setBall(100); //报错 showBox(box2); } public static void showBox(Box<Number> box){ Number ball = box.getBall(); System.out.println(ball); } }
类型通配符上限
语法:<? extends 实参类型>
表示使用时泛型的类型只能是实参类型或它的子类类型
public class Test { public static void main(String[] args) { ArrayList<A> listA = new ArrayList<A>(); ArrayList<B> listB = new ArrayList<B>(); ArrayList<C> listC = new ArrayList<C>(); //报错 print(listA); print(listB); print(listC); } public static void print(ArrayList<? extends B> list){ //do something } }
语法:<? super 实参类型>
表示使用时,泛型类型只能是实参类型或它的父类类型
/** * 假设类 C extends B extends A */ public class Test { public static void main(String[] args) { ArrayList<A> listA = new ArrayList<A>(); ArrayList<B> listB = new ArrayList<B>(); ArrayList<C> listC = new ArrayList<C>(); print(listA); print(listB); //报错 print(listC); } public static void print(ArrayList<? super B> list){ //do something } }
类型擦除
无限制类型的情况下擦除后是Object类型,也就是泛型类型的根父类
public class Box<E>{ private E ball; public E getBall() { return ball; } public Box(E ball) { this.ball = ball; } } ///////////类型擦除的结果/////////////// public class Box { private Object ball; public Object getBall() { return ball; } public Box(Object ball) { this.ball = ball; } }
上限通配符情况下,擦出后是上限类型
public class Eraser<T extends Number> { private T key; public T getKey() { return key; } public Eraser(T key) { this.key = key; } } /////////////类型擦除后//////////////// public class Eraser{ private Number key; public Number getKey() { return key; } public Eraser(Number key) { this.key = key; } }
泛型方法的类型擦除
public <T extends Number> T getValue(T value){ return value; } ///////类型擦除后///////////// public Number getValue(Number value){ return value; }
反射常用的泛型类
Class<T>
Constructor<T>