【Java数据结构】 第三章 —— 泛型

目录

 前言

  思维导图

             一、什么是泛型

             二、引出泛型

                         2.1 泛型的语法

             三、泛型类的使用

                         3.1 语法

                         ​​​​​​​3.2 示例

                         ​​​​​​​3.3 类型推导

             四、裸类型(Raw Type)(了解)

             五、泛型是如何编译的

             六、泛型的上界

                         ​​​​​​​6.1 语法

                         ​​​​​​​6.2 示例

             七、泛型方法

 写在最后


前言

       现在我们就来学习 Java数据结构 的第二个前置知识点 —— 泛型;

       现在,铁汁们就可以端起你们的小板凳,听我细细道来......

       下面,正文开始......

【Java数据结构】 第三章 —— 泛型_第1张图片


思维导图

【Java数据结构】 第三章 —— 泛型_第2张图片

 


一、什么是泛型

       一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类;如果要编写可以应用多种类型的代码,这种刻板的限制对代码的束缚就会很大。

                                                                                 —— 来源《Java编程思想》对泛型的介绍

       泛型是在JDK1.5引入的新的语法;

       通俗讲,泛型:就是广泛的应用于多种类型从代码上讲,就是对类型实现了参数化

即:类型作为参数进行传递。


二、引出泛型

第一个问题:实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据;

解答一:

       实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据:

class MyArray {
    public int[] array = new int[10];
}

//这个不可以,整形数组 只能存放 整型类型的数据

       但是我们知道,Object类 是任何类型的父类,那么 我们是不是可以把 整形数组 换成 Object类型的数组呢?——答案是可以的:

class MyArray {
    public Object[] array = new Object[10];
}

第二个问题:在第一个问题的基础上,需要根据成员方法返回数组中某个下标的值: 

解答二:

       根据成员方法返回数组中某个下标的值:

    //获取 pos下标的值
    public Object getPos(int pos) {
        return array[pos];
    }

    //给 pos下标放一个元素
    public void setPos(int pos,Object val) {
        array[pos] = val;
    }

       那么,我们就可以这样去调用:

【Java数据结构】 第三章 —— 泛型_第3张图片

       可是,这样的话就写的不好 —— 既然里面什么都可以放,那么我们就控制不了它了;

       比如说,想要获取下标是1的元素:

       为什么会报错呢?

       我们都知道1下标元素是一个字符串类型啊;

       可是编译器不知道,因为上面的 getPos方法的返回值是Object类型:【Java数据结构】 第三章 —— 泛型_第4张图片

       这就意味着,拿出的类型 可以是任何类型的数据(即使 自己知道是 字符串类型),可是编译器不知道(它认为拿出的数据可能是 继承于Object类中的任何一类);

       把 Object类型 赋给 String类型,父类 给 子类,向下转型,编译器不会编译通过;

       所以说,需要给编译器一个信号 —— 通过强制类型转换 使得编译器知道所拿出的数据是String类:

       所以说,这样的话就会有一个很大的缺点 —— 乱!!!!!!

       上面才放了两个元素,用肉眼可以看的来;

       如果放的元素很多,而且里面的元素时刻都在发生改变,

       那么,就算自己知道里面的类型,那么也会特别特别复杂;

       难道每一次都需要强制类型转换吗?

       这明显是不可能的......

以上代码实现后,我们会发现两个问题:

  1. 任何类型数据都可以存放;
  2. 每一次获取元素以后,都需要进行强制类型转换。

而我们所希望的是:

  1. 我们能不能自己去指定类型;
  2. 我们能不能 不再进行强制类型转换。

       于是,Java为了解决这个问题,就引入了泛型......

2.1 泛型的语法

//语法一:

class 泛型类名 <类型形参列表> {

}

//类型形参列表 一般使用大写字母来表示的;

//作为形参列表 的字母 可以有一个或者多个(无论有多少个,调用的时候都需要匹配)
//语法二:用于继承

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


}

//如:
class ClassName extends ParentClass {


} 

       随便插入一个:

       泛型在源码中的继承:

 

       于是,上面的代码可以这样修改:

package GenericDemo;

/**
 * :占位符 ——> 代表当前类是一个泛型类
 * @param 
 */

class MyArray  {

    public T[] array = (T[])new Object[10];//这个写法也不好,但是先保证不报错

    //获取 pos下标的值
    public T getPos(int pos) {
        return array[pos];
    }

    //给 pos下标放一个元素
    public void setPos(int pos,T val) {
        array[pos] = val;
    }

}

public class TestDemo1 {

    public static void main(String[] args) {

        MyArray myArray = new MyArray();
        myArray.setPos(0,1);
        myArray.setPos(1,2);

        Integer ret = myArray.getPos(0);
        System.out.println(ret);



    }
}

 

【注意】 

  1. <>里面的必须是 引用类型,即:<引用类型>:
  2. 第二个<>里面的 数据类型 可以省略: 

【了解】:

【规范】类型形参一般使用一个大写字母,常用的名称有:

  1. E 表示 Element;
  2. K 表示 Key;
  3. V表示 Value;
  4. N 表示 Number;
  5. T 表示 Type;
  6. S,U,V 等等 —— 第二、第三、第四个类型。


三、泛型类的使用

3.1 语法

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

3.2 示例

       【注意】泛型只能接受类,所有的基本数据类型 必须使用包装类(这个在上面也提到过)。

3.3 类型推导

       当编译器 可以根据上下文推导出类型实参时,可以省略类型实参(这个在上面也提到过):


四、裸类型(Raw Type)(了解)

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

       如:MyArray 就是一个裸类型:

【Java数据结构】 第三章 —— 泛型_第5张图片

        【注意】我们写的时候 不要写裸类型,裸类型是为了兼容 老版本的API保留机制。

       但是如果指定了裸类型的时候(本身是一个泛型类,但是没有用尖括号),那么就会出现一开始的老毛病:又要去进行强制类型转换之类的操作,非常的麻烦......

小结:

  1. 泛型是将数据类型参数化,进行传递;
  2. 使用表示当前类是一个泛型类;
  3. 泛型目前为止的优点:数据类型参数化,编译时自动进行 类型检查和转换。


五、泛型是如何编译的

       从而我们得出了一个结论:

       Java的泛型,是在编译的时候,擦除为了Object类型;

       而这种机制,叫做:擦除机制

       那么有的人就说了,既然编译的时候,把T替换成了Object,

       那么,为什么我执行以下代码的时候会报错:

       原因是这样子的:

       Java的数组很特殊(在堆上),在实例化数组的时候,T不是一个具体的类型;

       假设可以成功,那么上面的代码就会和这个代码类似:

        那么就又会出现 一开始出现的毛病:需要强制类型转换啥的......

 

如果有兴趣的话,可以去看一看有关擦除机制的传送门:

传送门:Java泛型擦除机制之答疑解惑


 

六、泛型的上界

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

6.1 语法

class 泛型类名<类型形参 extends 类型边界> {
 ......
}

【了解】如果没有指定类型 边界E,那么可以视为 E extends Object 。

6.2 示例

写一个泛型类,类中有一个方法,求学生年龄的最大值

方法的实现:

【Java数据结构】 第三章 —— 泛型_第6张图片 

 

调用:

【Java数据结构】 第三章 —— 泛型_第7张图片 

 

方法的实现部分 错误的原因是:

       在指定数组是T类型的时候,那么 <> 里面的Integer是 引用类型;

       而 引用类型 是不可以直接 用大于和小于号进行比较;

正确的方法是:可以去实现一下Comparable接口;

       但是,由于 在编译期间,T类型 又被擦除为了 Object类型,而Object类型 没有Comparable方法;

       所以又需要有一个上界,使用关键字extends(这个不是继承的意思):

package GenericDemo;
//写一个泛型类,类中有个方法,比较年龄的大小

class Alg> {
    public T findMax (T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }
}
class Student implements Comparable{
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}
public class TestDemo2 {

    public static void main(String[] args) {
        Alg alg = new Alg<>();
        Student[] students = new Student[3];
        students[0] = new Student("a",15);
        students[1] = new Student("b",25);
        students[2] = new Student("c",5);

        Student student = alg.findMax(students);
        System.out.println(student);
    }
}

注意:泛型没有所谓的下界!


七、泛型方法

       上面我们实现 求学生年龄最大值 的方法中,在调用的时候,需要先创建一个对象,然后需要去通过对象的引用 来调用findMax方法;

       那么,有没有一种可以不用去实例化对象,调用findMax的方法的办法呢?

       ——有,把方法写成 静态方法 就可以了:

       改成静态方法的时候,需要在static后面加上,否则会报错;

【Java数据结构】 第三章 —— 泛型_第8张图片 

 

【Java数据结构】 第三章 —— 泛型_第9张图片 


写在最后

泛型:

       只要知道了 到第二个标题的知识点,就可以去解决80%的集合的代码了;

       只要知道了 到第五个标题的知识点,就可以去看懂90%的集合的代码了;

       只要知道了 到第七个标题的知识点,就可以去看懂95%的集合的代码了;

       剩下的5%就属于进阶了 —— 通配符......

       嗯,通配符的话,现阶段可以不用做过多的要求......​​​​​​​

       ......

       由于博主水平有限,可能会出现一些表达不清楚,或者出现一些其他的情况,

       欢迎各位铁汁们指出来,和博主一起改正,

       一起努力,共同进步;

       好了,如果这篇博客对铁汁们有帮助的话,可以送一个免费的 赞 嘛;

       当然,顺手点个关注是再好不过的了......

【Java数据结构】 第三章 —— 泛型_第10张图片

你可能感兴趣的:(Java数据结构,——,主干部分,java,java数据结构,泛型,五一技术分享)