Java泛型

42 阅读3分钟

一、关于泛型

泛型程序设计(generic programming):意味着编写地代码可以对多种不同类型地对象重用。

泛型是一个表达能力很强地类型系统,允许设计者详细地描述变量和方法的类型如何变化。

1.1 为什么要使用泛型?

  • 在泛型类出现之前,程序员必须使用Object编写适用于多种类型地代码,这很烦琐,也很不安全。

1.2 如何使用泛型:

  • 类型参数
    public static void main(String[] args) {

        int num = 0;
        String str = "";

        // 定义类型参数为String的集合对象。
        List<String> list = new ArrayList<>();

        // 编译器可以检查,防止你插入错误的对象。
        list.add(num); // 报错,禁止添加该类型变量。
        list.add(str);
    }
  • 定义泛型类
/* *
 * 备注:常见的做法是类型变量使用大写字符。
 * Java库:使用变量E表示集合元素类型,K和V表示键和值的类型,T表示“任意类型”。
 * 
 * 则此处通过通配符<T>,定义泛型类。
 * 有setter,getter方法。
 */


public class Car<T> {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
    public static void main(String[] args) {

        // 通过通配符定义的Car类,更加灵活
        Car<Electric> electricCar = new Car<>();
        electricCar.setName("电动类型");

        Car<Fuel> fuelCar = new Car<>();
        fuelCar.setName("燃油类型");


        System.out.println(electricCar.getName());
        // print 电动类型
        System.out.println(fuelCar.getName());
        // print 燃油类型

    }
  • 定义泛型方法
    /* *
     * 通过通配符,定义一个泛型方法。
     * 类型参数:<T>
     * 返回值类型:T
     * 参数列表:T filed
     */

    private static <T> T getFiledValue(T filed) {
        return filed;
    }

    public static void main(String[] args) {

        // 不同类型的变量
        int num = 0;
        String str = "str";


        System.out.println(getFiledValue(num));
        // print 0
        System.out.println(getFiledValue(str));
        // print "str"
    }
  • 类型变量的限定
    /* *
     * 通过extends关键字,对类型参数进行限定。
     * 一个类型变量或通配符可以有多个限定。
     * extends限定可以有多个接口,用"&"分隔,但只能有一个类,且必须是排在第一位。
     */

    private static <T extends Integer> T getFiledValue(T filed) {
        return filed;
    }

    public static void main(String[] args) {

        // 不同类型的变量
        Integer num = 0;
        String str = "str";


        System.out.println(getFiledValue(num));
        System.out.println(getFiledValue(str)); // 类型不符,编译报错。
    }
  • 通配符限定拓展
        // 带有[子类型限定]的通配符,允许你[读取]一个泛型对象。
        List<? extends T>

        // 带有[超类型限定]的通配符,允许你[写入]一个泛型对象。
        List<? super  T>

二、泛型代码与虚拟机

虚拟机没有泛型类型对象,所有对象都属于普通类。

2.1 类型擦除:

  • 无论何时定义一个泛型类型,都会自动提供一个相应的原始类型。

  • 类型变量会被擦除,替换为其限定类型(或者,对于无限定的变量则替换为Object)。

2.2 需要记住:

  • 虚拟机中没有泛型,只有普通的类和方法。

  • 所有的类型参数都会替换为它们的限定类型。

  • 会合成桥方法来保持多态。

  • 为保持类型安全性,必要时会插入强制类型转换。

三、限制与局限

       // 注意:以下均为【错误示例】!!!

        // 1. 不能用基本类型作为类型参数!
        List<int> list = new ArrayList<>();

        // 2. 运行时类型查询只适用于原始类型!
        if (car instanceof Car<T>) {
        }

        // 3. 不能创建参数化类型的数组!
        int[] ints = new int <T>[10];

        // 4. 不能构造泛型数组!
        T[] array = new T[10];

        // 5. 不能在静态字段或方法中引用类型变量!
        private static T filed;

        private static T getfiled(){
            return filed;
        }

        // 6. 不能抛出也不能捕获泛型类对象!
        try {
            // do something ...
        } catch (T e) {
            System.out.println("...");
        }

        // 7. 当类型擦除后,不允许创建引发冲突的条件!
        // 譬如:当前类有equals()方法,类型被擦除后被当作Object类型,则调用的是Object.equals()方法。


四、结束语

“-------怕什么真理无穷,进一寸有一寸的欢喜。”

微信公众号搜索:饺子泡牛奶