数据结构初阶:Java泛型

1.什么是泛型

泛型:就是接受许多类型;通俗来说就是将类型参数化,你想传什么类型的值都行。

下面详细讲:

2.引出泛型

目标:实现一个类,类中有一个能存放任何数据类型的数组,并且成员方法可以返回对应下标的值。

数组是将相同类型的值聚在一起:int[] array = new int[5]; String[] str = new String[10];

这些数组类型都定死了,只要传其他类型的值就报错。

这时我们想到类都继承于Object,写一个Object类型的数组就可以接收所有类型的值,如:

看到这里,我们意识到一个问题 。我不知道要用什么类型来向下转型呀!这确实可以接收各种类型,但是这也太杂了;一个数组里有各种基本类型、各种引用类型,我们能否指定类对象只能接收哪个(哪些)类呢?------》Java就设计了泛型这一概念!!!

3.语法

java 复制代码
class 泛型类名称<类型形参列表> {
//这里可以使用类型参数
}

class ClassName<>{

}

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

class ClassName<T1,T2,...,Tn> extends ParentClass<T1>{

}

上述图片代码改写如下:

java 复制代码
class MyArray<T>{
    Object[] objects = new Object[10];

    public T getElement(int index) {
        return (T)objects[index];
    }
    public void setElement(int index,T val){
        objects[index]=val;
    } 
}

代码解释:

  1. 类名后的 <T> 代表占位符,表示当前类是一个泛型类。(实际上可以使用任意大写字母)
    了解:【规范】类型形参常用的名称有:
    E 表示 Element
    K 表示 Key
    V 表示 Value
    N 表示 Number
    T 表示 Type
    S, U, V 等等 - 第二、第三、第四个类型

3.1泛型类的使用

  • 定义泛型类的引用:泛型类<类型实参> 变量名 ;
  • 实例化泛型类对象:new 泛型类<类型实参>(构造方法实参); //这里的类型实参不写也行,因为系统会根据引用的类型实参进行推导。

示例:

MyArray<Integer> list = new MyArray<Integer>();

//泛型类型不能是基本类型!!!

3.2泛型类引用的类型

先看下面的实操了解代码:

Test2.main()中的myArray是什么类型的?

因为MyArray继承了Object类,所以我们在打印时,会调用父类的toString()方法;

该方法返回调用引用的类型;


结论:可以看出myArray的类型还是MyArray,不是MyArray<Integer>!!!

小结:

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

4.实操

既然对类对象指定了输入数据的类型,那是否还能输入其他类型的数据?

答案是不能!!

5.擦除机制

泛型到底是怎样进行编译的呢?

其实,泛型是编译时期的一种机制,在运行期间是没有泛型这一概念的。

下图就很直观:


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

有同学就要问了:那泛型有什么用呢?

答:编译时自动进行类型检查和转换。

6.泛型的上界

在定义泛型类时,有时需要对传入的类型进行约束。

6.1语法

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

例:

java 复制代码
public class ClassName<E extends Number> {
    //......
}

代码解读:只有类型是Number或者Number的子类才能作为E的类型形参。(最多只能是Number类,这就是泛型的上界)

例:

代码解读:String不是Number的子类,所以当将String作为泛型实参时就会报错!!

7.泛型进阶

7.1通配符

? 用于在泛型的使用,即为通配符
一个例子让你明白通配符的作用:

java 复制代码
class Message<T>{
    private T message ;

    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}

public class TestDemo2 {
    public static void main(String[] args) {
        Message<String> message = new Message<>() ;
        message.setMessage("不上早八!");
        func(message);
    }
    
    public static void func(Message<String> temp){
        System.out.println(temp.getMessage());
    }
}

上面的代码问题在于func(),这个方法写死了,当我泛型类型不是String,而是其它就会出问题,图例:

所以我想func()什么泛型类型都能接受,就用到通配符(?)。代码改成如下:

java 复制代码
public class TestDemo2 {
    public static void main(String[] args) {
        Message<String> message = new Message<>() ;
        message.setMessage("不上早八!");
        func(message);

        Message<Integer> message2 = new Message<>() ;
        message2.setMessage(123);
        func(message2); //不报错
    }

    public static void func(Message<?> temp){//什么泛型类型都能接受了
        System.out.println(temp.getMessage());
    }
}

7.1.1通配符和<E>的区别

通配符是可以在泛型类之外使用的,但E等大写字母,必须在泛型中使用。(<大写字母>是泛型标识)

7.2通配符的上界

语法:

java 复制代码
<? extends 上界>
<? extends Number> //解读:只能接收Number或者是Number的子类类型

7.2.2实操

将用下面的关系做示例

示例1:

java 复制代码
class Biology {
}
class Animal extends Biology {
}
class Dog extends Animal {
}
class Cat extends Animal {
}


class Message<T> { // 设置泛型
    private T message ;
    public T getMessage() {//获取
        return message;
    }
    public void setMessage(T message) {//设置
        this.message = message;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Message<Dog> message = new Message<>() ;
        message.setMessage(new Dog());
        fun(message);

        Message<Cat> message2 = new Message<>() ;
        message2.setMessage(new Cat());
        fun(message2);
    }

    public static void fun(Message<? extends Animal> message){
        System.out.println(message.getMessage());
        message.getMessage();
    }
}

注意1:

通配符可不像<E>,你不能写成 ? element = get....(); 所以你不知道传入了什么泛型类型

注意2:

所以我们在传入时要停下来想想了。

总结: 通配符的上界,不能进行写入数据,只能进行读取数据

7.3通配符的下界

语法:

<? super 下界>

<? super Number> //解读:只接收Number或者Number的父类

示例·:

通配符的下界与上界截然相反:

java 复制代码
public static void main(String[] args) {
        Message<Animal> message = new Message<>() ;
        message.setMessage(new Animal());
        fun(message);

        Message<Biology> message2 = new Message<>() ;
        message2.setMessage(new Biology());
        fun(message2);
    }

    public static void fun(Message<? super Animal> message){//只接受Animal或者它的父类
        System.out.println(message.getMessage());

        message.setMessage(new Dog());//此时可以修改(传入数据),因为Dog、Cat都是Animal子类
        message.setMessage(new Cat());
    }

这放随便放,但取就要小心了!!!

7.3总结

总结:通配符上界取随便取,放值要思考;通配符下界放随便放,但取就要注意。

相关推荐
点云SLAM1 小时前
算法与数据结构之二叉树(Binary Tree)
数据结构·算法·二叉树·深度优先·广度优先·宽度优先
小龙报1 小时前
《算法通关指南:算法基础篇 --- 一维前缀和 — 1. 【模板】一维前缀和,2.最大子段和》
c语言·数据结构·c++·算法·职场和发展·创业创新·visual studio
.柒宇.3 小时前
力扣hoT100之找到字符串中所有字母异位词(java版)
java·数据结构·算法·leetcode
王璐WL4 小时前
【数据结构】单链表的经典算法题
数据结构·算法
Zzzzmo_5 小时前
Java数据结构:二叉树
java·数据结构·算法
聆风吟º5 小时前
【数据结构入门手札】数据结构基础:从数据到抽象数据类型
数据结构·数据类型·逻辑结构·数据对象·物理结构·数据项·数据元素
啊吧怪不啊吧5 小时前
二分查找算法介绍及使用
数据结构·算法·leetcode
立志成为大牛的小牛7 小时前
数据结构——四十二、二叉排序树(王道408)
数据结构·笔记·程序人生·考研·算法
开开心心_Every9 小时前
专业视频修复软件,简单操作效果好
学习·elasticsearch·pdf·excel·音视频·memcache·1024程序员节
摇滚侠15 小时前
StreamAPI,取出list中的name属性,返回一个新list
数据结构·list