Java 泛型总结

161 阅读5分钟

概念

泛型是JDK5引入的一个特性,在编译时进行类型安全检测,本质是类型参数化,引入的泛型的意义在于:

  • 实现代码复用。
  • 类型安全检测,对类型进行约束,在使用过程中不需要进行类型强转。

泛型类型

泛型类型分为三类:泛型类、泛型接口、泛型方法。

  • 泛型类
public class GenericClassTest {
    /**
     * 泛型类
     * @param <T>
     */
    public static class Result<T> {
        private T data;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }
    }

    public static void main(String[] args) {
        Result<String> result = new Result<>();
        result.setData("这是一个泛型类:参数类型是String");
        System.out.println(result.getData());
    }
}
  • 泛型接口
public class GenericInterfaceTest {
    /**
     * 泛型接口
     * @param <I> 输入类型
     * @param <R> 结果类型
     */
    public interface ITransform<I, R>{
        /**
         * 输入指定的类型
         * @param input 输入类型
         * @return 返回结果类型
         */
        R getResult(I input);
    }

    static class Transform implements ITransform<Integer, String> {

        @Override
        public String getResult(Integer input) {
            return "Input为输入的类型,Result为输出的类型,输出"+input;
        }
    }

    public static void main(String[] args) {
        ITransform<Integer, String> transform = new Transform();
        System.out.println(transform.getResult(3));
    }

}
  • 泛型方法
public class GenericMethodTest {

    static class Base{
        private String value;

        public Base() {
            this.value = "初始化";
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
    /**
     * 泛型方法
     *
     * @param t   参数
     * @param <T> 标记入参的类型
     * @return 返回E类型
     */
    public static <T> T newInstance(Class<T> t) throws Exception {
        // 返回一个对象实例
        return t.newInstance();
    }


    public static <E> E[] newArray(E... e) {
        // 一个生成泛型数组的取巧方法
        return e;
    }

    public static <E> E[] generateArray(Class<E> eClass, int size) {
        // 泛型数据不能直接创建
        // E[] arrays = new E[size];
        // 合理使用
        return (E[]) Array.newInstance(eClass, size);
    }

    public static void main(String[] args) throws Exception {
        Base base = newInstance(Base.class);
        System.out.println(base.getValue());
        Integer[] array = newArray(1,2,4);
        System.out.println(Arrays.toString(array));
        String[] strings = generateArray(String.class, 10);
        System.out.println(strings.length);
        strings[0] = "数组";
        System.out.println(Arrays.toString(strings));
    }
}

常用通配符

泛型通配符的名称都是用来表示一个Java类型,没什么太大的区别,通常用一个大写字母来表示。常用的字母如下:

  • T(type) 表示一个Java类型如Consumer<T>
  • U、V 位于T后面,可认为是第二个类型,第三个类型,如BiPredicate<T, U>
  • R(Result) 表示一个结果类型,如BiFunction<T, U, R>
  • E(Element) 在集合中使用,如ArrayList<E>
  • K V(Key Value) 表示一个键值对,如HashMap<K,V>
  • S(Stream) 表示一个流,如BaseStream<T, S>
  • N(Number) 数值类型
  • ? 表示一个未知类型

类型擦除

类型擦除(Type Erasure)是Java的一种编译时机制,用于实现泛型的类型安全,并在运行时擦除泛型的具体类型信息(运行时具体类型不可用),即泛型类型参数被替换为具体类型的第一个上界类型或者Object类型,主要目的是为了保持与Java的早期版本代码的兼容性。

public class GenericTypeErasureTest {
    static class TypeErasure<T,U>{
        // 类型擦除 参数为(Object value)
        public void setValue(T value) {
            System.out.println(value);
        }
        
        // 类型擦除 参数为(Object value)
        public void setValue(U value) {
            System.out.println(value);
        }
    }
    static class TypeErasurePass<T extends Number,U>{
        // 类型擦除,参数为 (Number value)
        public void setValue(T value) {
            System.out.println(value);
        }

        // 类型擦除,参数为 (Object value)
        public void setValue(U value) {
            System.out.println(value);
        }
    }
    
}

类型限定

类型限定是为泛型的类型参数确定边界,对该泛型的使用范围进行一个约束。

  • <T extend Animal> 用于修饰类、接口、方法
  • ? extends Animal 有外围类才能使用,如List<? extends Animal>
  • ? super Cat 有外围类才能使用,如List<? super Feline>

一个类型变量或者通配符可以有多个限定,使用&分隔类型变量,但是只能有一个限定类(class)其他为接口(interface),如

class MultiClass<T extends Number & Comparable<T> & Serializable> {
    private T data;
}

PECS原则: producer extends consumer super

《Effictive Java》描述中,为了获取最大的灵活性,要在生产者和消费者的输入参数上添加边界,使得生产有上限,消费有下线,也就是PECS原则。

public class GenericQualifyTest {
    static class Animal {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void description() {
            System.out.println("动物描述");
        }
    }

    static class Feline extends Animal {
        public Feline() {
            super.setName("猫科动物");
        }

        @Override
        public void description() {
            System.out.println("Feline is Cat upper bound");
        }
    }

    static class Cat extends Feline {
        public Cat() {
            super.setName("小猫咪");
        }

        @Override
        public void description() {
            System.out.println("猫会喵喵叫");
        }
    }

    static class Primate extends Animal {
        public Primate() {
            super.setName("灵长类动物");
        }

        @Override
        public void description() {
            System.out.println("Primate is Monkey upper bound");
        }
    }

    /**
     * 限定上界类
     *
     * @param <T> Animal类及其子类
     */
    static class UpperClass<T extends Animal> {
        public T data;

        public UpperClass(T data) {
            this.data = data;
        }

        public UpperClass() {
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }
    }

    static <T extends Feline> T getFeline(Class<T> tClass) 
            throws InstantiationException, IllegalAccessException {
        return tClass.newInstance();
    }


    /**
     * 测试类型上限
     *
     * @throws Exception
     */
    static void testTypeUpper() throws Exception {
        //        getFeline(Animal.class); // ERROR 限定类型
        Feline feline = getFeline(Feline.class);
        Cat cat = getFeline(Cat.class);
        feline.description();
        cat.description();

//        UpperClass<Integer> upperClass = new UpperClass<Integer>(); // ERROR 限定了类型
//        UpperClass<Cat> upperClass = new UpperClass<>(); // 使用该行 upperClass.setData(feline); 无法注入
        UpperClass<Animal> upperClass = new UpperClass<>();
        upperClass.setData(cat);
        upperClass.setData(feline);
    }


    static void testConsumer() {
        System.out.println("=============测试消费者==============");
        List<? super Feline> felines = new ArrayList<>();
        felines.add(new Cat());
        Object object = felines.get(0);
        ((Cat) object).description();

        List<UpperClass<? super Feline>> upperClassList = new ArrayList<>();
        UpperClass<Animal> animalUpperClass = new UpperClass<>();
        Animal animal = new Animal();
        animalUpperClass.setData(animal);
        Cat cat = new Cat();
        UpperClass<Cat> catUpperClass = new UpperClass<>();
        catUpperClass.setData(cat);

        UpperClass<Primate> primateUpperClass = new UpperClass<>();
        primateUpperClass.setData(new Primate());

        UpperClass<Feline> felineUpperClass = new UpperClass<>();
        felineUpperClass.setData(new Feline());

        upperClassList.add(felineUpperClass);
        upperClassList.add(new UpperClass<>(new Primate())); // 这种实例化似乎跳过了编译检查
//        upperClassList.add(catUpperClass); // ERROR 确定下界,从Feline开始
//        upperClassList.add(primateUpperClass);
        UpperClass<? super Feline> upperClass = upperClassList.get(0);
        Animal data = upperClass.getData(); // 显示上界类型
        data.description();

        List<? super UpperClass<? super Feline>> superSuperList = new ArrayList<>();
        superSuperList.add(animalUpperClass);
        Object superSuperObject = superSuperList.get(0);
        ((UpperClass<Animal>) superSuperObject).getData().description();
        // ERROR UpperClass<? super Feline> 确定UpperClass类型参数Feline为下界
        // ? super UpperClass<? super Feline> 是确定UpperClass为下界,所以就是Object
//        superSuperList.add(catUpperClass);
//        superSuperList.add(primateUpperClass);


        List<? super UpperClass<? extends Feline>> superExtendsList = new ArrayList<>();
        superExtendsList.add(catUpperClass);
        Object superExtendsObject = superExtendsList.get(0);
        ((UpperClass<Cat>) superExtendsObject).getData().description();
        // ERROR UpperClass<? extends Feline> 确定UpperClass类型参数Feline为上界
        // <? super UpperClass<? extends Feline> 是确定UpperClass为下界,所以就是Object
//        superExtendsList.add(animalUpperClass);
//        superExtendsList.add(primateUpperClass);


    }

    static void testProducer() {
        System.out.println("===========测试生产者==============");
        UpperClass<? extends Feline> felineUpperClass = new UpperClass<>();
//        felineUpperClass.setData(new Feline());// 向下转型,不能确定类型
//        felineUpperClass.setData(new Primate());
        Feline data = felineUpperClass.getData();
        System.out.println(data); // 空数据

        List<UpperClass<? extends Feline>> upperClasses = new ArrayList<>();
        upperClasses.add(new UpperClass<>(new Cat()));
//        upperClasses.add(new UpperClass<>(new Animal()); // 确定上界,由Feline开始
//        upperClasses.add(new UpperClass<>(new Primate()));
        for (UpperClass<? extends Feline> upperClass : upperClasses) {
            upperClass.getData().description();
        }

        List<? extends UpperClass<? extends Feline>> extendsExtendsList 
                = new ArrayList<UpperClass<? extends Feline>>() {{
            UpperClass<Cat> upperClass = new UpperClass<>();
            upperClass.setData(new Cat());
            this.add(upperClass);
//            this.add(new UpperClass<>(new Primate())); // 限定类型
        }};

        for (UpperClass<? extends Animal> upperClass : extendsExtendsList) {
            upperClass.getData().description();
        }
        // 向下转型,无法确认类型
//        extendsExtendsList.add(new UpperClass<>(new Cat()))
//        extendsExtendsList.add(new UpperClass<>(new Primate()));
        List<? extends UpperClass<? super Feline>> extendsSuperList 
                = new ArrayList<UpperClass<? super Feline>>() {{
            UpperClass<Animal> animalUpperClass = new UpperClass<>();
            animalUpperClass.setData(new Animal());
            UpperClass<Cat> catUpperClass = new UpperClass<>();
            catUpperClass.setData(new Cat());

            UpperClass<Primate> primateUpperClass = new UpperClass<>();
            primateUpperClass.setData(new Primate());

            UpperClass<Feline> felineUpperClass = new UpperClass<>();
            felineUpperClass.setData(new Feline());
            this.add(animalUpperClass);
            this.add(felineUpperClass);
            this.add(new UpperClass<>(new Primate())); // 这种实例化似乎跳过了编译检查
//            this.add(catUpperClass); // ERROR
//            this.add(primateUpperClass); // ERROR
        }};

        for (UpperClass<? super Feline> upperClass : extendsSuperList) {
            Animal animal = upperClass.getData();
            animal.description();
        }
        // 向下转型,无法确定类型
//        extendsSuperList.add(new UpperClass<>(new Cat()));

    }


    public static void main(String[] args) throws Exception {
        testTypeUpper();
        testConsumer();
        testProducer();
    }

}

泛型的约束和局限性

  1. 不能用基本类型实例化类型参数Generic<int[]> generic = new Generic<>(),类型擦除
  2. 不能创建参数化类型数组 Generic<String>[] generics = new Generic<String>[10],但是Generic<String>[] generics = new Generic[10]是合法的
  3. 运行时类型查询只适用原始类型,Pair<String>只能 查询到Pair,得不到<>里的类型
  4. 不能使用new实例化类型变量T t= new T()
  5. 不能直接构造泛型数组T[] ts = new T[10]
  6. 泛型类的静态上下文中类型变量无效class Generic<T> {private static T staticT}
  7. 不能抛出或捕获泛型类实例,扩展Throwable都是不合法的(类型擦除),但是在异常规范使用类型变量是被允许的,即<T extends Throwable> void handle(T t) throws T
public class GenericRestrainTest {
    static class Generic<T> {
        // 静态变量没有实例化,无法知道类型
        private static T staticT;
        private T data;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }
    }

    static <T> void newInstance() {
        T t = new T(); // 不能使用new创建实例
        T[] ts = new T[10]; // 不能构建泛型数组
    }

    public static void main(String[] args) {
        Generic<int[]> generic = new Generic<>();
        int[] a = new int[5];
        generic.setData(a);
        System.out.println(Arrays.toString(generic.getData()));

//        Generic<int> generic1 = new Generic<int>(); // ERROR 类型参数不能使用基本类型
        Generic<String>[] genericsMatchs = new Generic<String>[10]; // 编译出错
//        Generic<String>[] generics = new Generic<>[10]; // 运行出错

        Generic<String>[] generics = new Generic[10]; // OK 但是会有警告
        Generic<String> stringGeneric = new Generic<>();
        stringGeneric.setData("generic");
        Generic<Integer> integerGeneric = new Generic<>();
        integerGeneric.setData(10);

        generics[0] = stringGeneric;
//        generics[1] = integerGeneric; // 限定了String

        System.out.println(generics[0].getData());

        Generic<?>[] generics1 = new Generic[10];
        generics1[0] = stringGeneric;

        generics1[2] = integerGeneric;
        System.out.println(generics1[0].getData());

        Generic<?>[] generics2 = new Generic<?>[10];
        Generic<String>[] generics3 = new Generic<?>[10]; // 编译出错
        Generic<String>[] generics4 = (Generic<String>[]) new Generic<?>[10]; // 强转

    }

    public static <T extends Throwable> void handle(T t) throws T {
        try {
        } catch (Throwable actuallyCause) {
            t.initCause(actuallyCause);
            throw t;
        }
        // 不能在catch中使用泛型变量
        catch (T throwable) {}
    }

}

参考文档

《Java核心技术卷 Ⅰ》

pdai 泛型机制详解