背景
先来看一个例子:
public static void main(String[] args) {ArrayList list = new ArrayList();list.add("java");list.add(100);list.add(true);for(int i = 0; i < list.size(); i++) {Object obj = list.get(i);String str = (String)obj;System.out.println(str);}}
以上程序在编译期间没有问题,而在运行出错,报java.lang.ClassCastException,提示说Integer类型不能转换为String类型。

添加泛型:
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("java");list.add("python");list.add("golang");for(int i = 0; i < list.size(); i++) {String str = list.get(i);System.out.println(str);}}
声明集合中的类型为String类型,不会报错。

泛型类

定义一个泛型类的例子:
public class Generic<T> {// key这个成员变量的类型为T,T的类型由外部类调用该类时指定private T key;// 泛型构造方法形参key的类型也为T,T的类型由外部指定public Generic(T key) {this.key = key;}public T getKey(){return key;}}
T:是由外部使用类使用该类对象时指定。

编写上面泛型类的测试方法:
// 只编写了一个泛型类 Generic,就达到了代码复用Generic<String> gen_str = new Genertic<>("abc");String k1 = gen_str.getKey();System.out.println("k1: " + k1);Generic<Integer> gen_int = new Genertic<>(123);int k2 = gen_int.getKey();System.out.println("k2: " + k2);// 泛型类在创建对象时,没有指定类型,将按照Object类型来操作。Generic gen_obj = new Genertic(123);Object k3 = gen_obj.getKey();System.out.println("k3: " + k3);// 泛型类不支持基本数据类型,即<>中不能为int、long、float等// 同一个泛型类,如Generic,根据不同的数据类型创建的对象,本质上是同一类型。System.out.println(gen_str.getClass()); // 打印Generic的类路径System.out.println(gen_int.getClass()); // 打印Generic的类路径(和上一句一样)
泛型类实际案例
需求:公司年会上有抽奖品的,也有抽现金的,编写一个抽奖器类完成功能
// 抽奖器类public class ProGetter<T> {Random random = new Random();// 奖品,奖品(String)或奖金(int)private T product;// 奖品池List<T> list = new ArrayList<>();// 添加奖品到奖品池public void addProduct(T t) {list.add(t);}// 抽奖public T getProduct(){list.get(random.nextInt(list.size()));return product;}}// 测试类class TestProduct{// 创建抽奖器对象,指定数据类型ProGetter<String> str_pro = new ProGetter<>();String[] pros = {"iphone 13", "Huawei P50", "Lenovo Y900P", "Beats"};for(String s : pros) {str_pro.addProduct(s);}// 抽奖String pro = str_pro.getProduct();System.out.println("恭喜您,你抽中了: " + pro);//-------------------------------------------------ProGetter<Integer> int_pro = new ProGetter<>();int[] cash = {3000, 5000, 666, 888, 10000};for(int c : cash) {int_pro.addProduct(c);}// 抽奖int pro = int_pro.getProduct();System.out.println("恭喜您,你抽中了: " + pro);}
泛型类派生子类

泛型接口


看个具体例子:
// 定义一个泛型接口public interface Generator<T> {T getKey();}// 实现了泛型接口的类,实现类不是泛型类(上面的情况一)public class Apple implements Generator<String> {@Overridepublic String getKey() {return "hello";}}
// 实现类也是泛型类(上面情况二)// 泛型类的类型还可以扩充,如<T, E, K>,但是必须得包含接口的泛型类型,否则编译报错public class Pair<T> implements Generator<T> {private T key;@Overridepublic T getKey() {return key;}}
泛型方法

上面泛型类中定义的方法的返回值定义为T类型,在此声明,此方法不是泛型方法,如上面的代码块中定义的Pair泛型类中的getKey成员方法就不是泛型方法。
注意点:
例子:
// 抽奖器类public class ProGetter<T> {Random random = new Random();// 奖品,奖品(String)或奖金(int)private T product;// 奖品池List<T> list = new ArrayList<>();// 添加奖品到奖品池public void addProduct(T t) {list.add(t);}// 抽奖,不是泛型方法public T getProduct(){list.get(random.nextInt(list.size()));return product;}/**定义一个泛型方法<E> 是泛型标识,是具体类型,由调用方法的时候指定。*/public <E> getProduct(ArrayList<E> list) {return list.get(random.nextInt(list.size()));}}// 测试类public class TestPro {// 创建抽奖器对象,指定数据类型ProGetter<Integer> pro = new ProGetter<>();// 调用泛型类的成员方法,方法的返回值类型必须跟泛型类定义的类型Integer一致int[] cash = {3000, 5000, 666, 888, 10000};for(int c : cash) {pro.addProduct(c);}Integer product = pro.getProduct();System.out.println(product + "\t" + product.getClass().getSimpleName());//*************************************************************************ArrayList<String> strList = new ArrayList<>();list.add("手机");list.add("服务器");list.add("显卡");// 调用泛型方法,类型通过调用方法时指定String prize = pro.getProduct(strList);System.out.println(prize + "\t" + prize.getClass().getSimpleName());}
泛型方法支持可变参数
// 定义一个是可变参数的且是静态的泛型方法public static <E> void print(E... e) {for(int i = 0; i < e.length; i++) {System.out.print(i + "\t");}}// 测试一下:ProGetter。print(1, 2, 3, 4, 5);

// 静态的泛型方法,采用多个泛型public static <T, E, K> void printType(T t, E e, K k) {System.out.println(t + "\t" + t.getClass().getSimpleName());System.out.println(e + "\t" + e.getClass().getSimpleName());System.out.println(k + "\t" + k.getClass().getSimpleName());}// 测试一下ProGetter.printType(100, "java", true);
类型通配符

例子:
public class Box<E> {private E first;public E getFirst() {return first;}public void setFirst() {this.first = first;}}// 测试public class TestBox{public static void showBox(Box<Number> box) {Number first = box.getFirst();System.out.println(first);}public static void showBox(Box<Integer> box) {Number first = box.getFirst();System.out.println(first);}// 上面两个方法不叫重载,会报错,只能存在一个,使用泛型通配符解决// ? 表示可以传递任意类型public static void showBox(Box<?> box) {Object first = box.getFirst();System.out.println(first);}}
类型通配符的上限

class Animal{}class Cat extends Animal{}class MiniCat extends Cat{}// 测试public class TestUp{public static void showAnimal(ArrayList<? extends Cat> list) {for(int i = 0; i < list.size(); i++) {Cat cat = list.get(i);System.out.println(cat);}list.add(new Cat()); // 报错,上线通配符不能添加元素list.add(new miniCats); // 同样报错}public static void main(String[] args){ArrayList<Animal> animals = new ArrayList<>();ArrayList<Cat> cats = new ArrayList<>();ArrayList<MiniCat> miniCats = new ArrayList<>();showAnimal(animals); //报错,上限只能是CatshowAnimal(cats);showAnimal(miniCats);}}
类型通配符的下限

class Animal{}class Cat extends Animal{}class MiniCat extends Cat{}// 测试public class TestDown{public static void showAnimal(ArrayList<? extends Cat> list) {for(int i = 0; i < list.size(); i++) {Cat cat = list.get(i);System.out.println(cat);}list.add(new Cat()); // 下限通配符添加元素不会报错list.add(new Animal()); // 不会报错}public static void main(String[] args){ArrayList<Animal> animals = new ArrayList<>();ArrayList<Cat> cats = new ArrayList<>();ArrayList<MiniCat> miniCats = new ArrayList<>();showAnimal(animals);showAnimal(cats);showAnimal(miniCats); //报错,下限只能是Cat}}
jdk中下线通配符的使用
类型擦除

java中所有的泛型信息只存在于编译阶段,编译完成后,所有的泛型信息将被擦除掉
无限制类型擦除

泛型无限制类型擦除,编译结束后,把T类型全部替换为Object类型
可以通过反射来得到编译后T类型被擦除后得到的类型:
// 通过反射,拿到Erasure的key的数据类型class TestErasure{psvm(){Erasure<Integer> erasure = new Erasure<>();// 利用反射,获取Erasure类的字节码文件的Class类对象Class<? extends Erasure> clz = erasure.getClass();// 获取所有成员变量Field[] fields = clz.getDeclaredFields();for(Field field : fields) {// 打印成员变量的名称和类型System.out.println(field.getName() + ":" + field.getType().getSimpleName());}}}运行结果:// key:Object
有限制类型擦除

// 通过反射,拿到Erasure的key的数据类型class TestErasure{psvm(){Erasure<Integer> erasure = new Erasure<>();// 利用反射,获取Erasure类的字节码文件的Class类对象Class<? extends Erasure> clz = erasure.getClass();// 获取所有成员变量Field[] fields = clz.getDeclaredFields();for(Field field : fields) {// 打印成员变量的名称和类型System.out.println(field.getName() + ":" + field.getType().getSimpleName());}}}运行结果:// key:Number
擦除泛型方法中类型定义的参数

public class Erasure<T extends Number> {....;// 添加泛型方法public <T extends Number> T show(T t) {return t;}}class TestErasure{psvm(){Erasure<Integer> erasure = new Erasure<>();// 利用反射,获取Erasure类的字节码文件的Class类对象Class<? extends Erasure> clz = erasure.getClass();// 获取所有成员方法Methods[] methods = clz.getDeclaredMethods();for(Method method : methods) {// 打印成员方法的名称和返回值类型System.out.println(method.getName() + ":" + mathod.getReturnType().getSimpleName());}}}// 结果:getKey:Numbershow:ListsetKey:void
桥接方法

// 编写泛型接口和实现类// 测试类psvm{// 利用反射,获取InfoImpl类的字节码文件的Class类对象Class<InfoImpl> infoClass = InfoImpl.class;// 获取所有成员方法Method[] methods = infoClass.getDeclaredMethods();//for(Method method : methods) {// 打印成员方法的名称和返回值类型System.out.println(method.getName() + ":" + mathod.getReturnType().getSimpleName());}}// 结果:info:Integerinfo:Object (多了个方法,编译器替我们去生成的)
泛型数组

真正开发中一般都是用泛型集合,很少使用泛型数组。

