一篇文章理解泛型(包含面试题)

71 阅读4分钟

泛型

定义

泛型:就是适用于许多许多类型。

为什么要有泛型?

1.当我们要写数字的时候,我们给它int类型;当我们要写姓名的时候,我们给它String类型;又或者是我们自己定义的student类等等。

2.上面的给类型操作往往会造成一种无形的束缚,为了摆脱这种束缚,数据结构中就存在了可以应用于多种类型的代码 。这种代码就是泛型——对类型实现了参数化。

具体例子(数组)

我们以前学过的数组,只能存放指定类型的元素,例如:int[] array = new int[10]; String[] strs = new String[10];

我们知道所有类的父类, 默认为Object,那我们可以尝试创建一个Object类的数组。

特点

  1. 任何类型数据都可以存放

  2. 1号下标本身就是字符串,但是确编译报错。必须进行强制类型转换

虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。 此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

泛型语法

把类型,作为参数传递。需要什么类型,就传入什么类型。

第一种
class 泛型类名称<类型形参列表> {
    // 这里可以使用类型参数
}

例子:
class ClassName<T1, T2, ..., Tn> {
}

第二种
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
    // 这里可以使用类型参数
}

例子:
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
    // 可以只使用部分类型参数
}
  • 类名后的 < T > 代表占位符,表示当前类是一个泛型类 ,可以写多个。
  • E 表示 Element (元素)
  • K 表示 Key (关键字)
  • V 表示 Value (值)
  • N 表示 Number (数字)
  • T 表示 Type (类型)

此时代码变为:

  • 注释1:不能new泛型类型的数组 T[] ts = new T[5]; 是不对的
  • 注释2:实例化中写的这个""指定当前类型,最右边<>编译器会在存放元素的时候帮助我们进行类型检查。

泛型类的使用

泛型类<类型实参> 变量名 = new 泛型类<类型实参>(构造方法实参);

  • MyArray list = new MyArray()
  • 泛型只能接受类,所有的基本数据类型必须使用包装类!

裸泛型(了解)

裸类型是一个泛型类但没有带着类型实参

注意: 我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制

泛型是如何编译的(擦除机制)面试题

泛型是一种在编译时期的机制,我们称之为擦除机制

通过命令:javap -c 查看字节码文件,所有的T都是Object

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。

那为什么,T[] ts = new T[5]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new Object[5]吗?

因为编译器认为不安全,为什么不安全?请看下面这张图

泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。

语法

语法进阶版

当我们要实现一个求数组中最大值的泛型类

泛型方法

泛型当中的方法

语法

实例

实现一个求数组中最大值的泛型类

//泛型方法,对方法中的类型形参列表进行修改
//方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
class Alg2 {
    public <T extends Comparable<T>> T findMaxVal(T[] array) {
        T max = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
}

public static void main(String[] args) {
    Alg2 alg2 = new Alg2();//实例化
    Integer[] array = {1,2,3,7,8,6};
    alg2.<Integer>findMaxVal(array);//<>可以不写,能够通过array的类型推导出来为Integer
    alg2.findMaxVal(array);
}

不实例化的写法

/对方法加入static的时候,就不需要实例化了
class Alg3 {
    public static <T extends Comparable<T>> T findMaxVal(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
}

    public class Test {

    //不用实例化的泛型方法: Alg3
        public static void main(String[] args) {
            //可以不实例化
            Integer[] array = {1,2,3,7,8,6};
            System.out.println(Alg3.<Integer>findMaxVal(array));
            System.out.println(Alg3.findMaxVal(array));
        }

Alg-Alg2-Alg3明显的区别

泛型总结

1.泛型是将数据类型参数化,进行传递。

2.使用 < T > 表示当前类是一个泛型类。

3.泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换。

4.裸泛型不建议使用,是为了兼容老版本。

5.擦除机制是把**所有的T替换为Object。

6.泛型只有上界,没有下界。

7.泛型方法简单的运用(Comparable)。