IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构与算法----泛型 -> 正文阅读

[数据结构与算法]数据结构与算法----泛型

目录

1. 什么是泛型

2. 泛型的引入

3.泛型

3.1 泛型的语法

3.2 泛型的引用

3.3 泛型的上界?

4. 泛型方法

?5. 通配符

?5.1通配符的上界

5.2 通配符的下界

4. 拓展


1. 什么是泛型

一般的类和方法,只能使用具体的类型 : 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的 代码,这种刻板的限制对代码的束缚就会很大。----- 来源《 Java 编程思想》对泛型的介绍。
泛型是在 JDK1.5 引入的新的语法,通俗讲,泛型: 就是适用于许多许多类型 。从代码上讲,就是对类型实现了参数化。

2. 泛型的引入

我们在之前的数组的学习中,我们知道,数组的类型我们给定之后,我们在数组中存放的数据他的类型就是我们所给的数组的类型

?我们没有办法去存放其他类型的数据元素,如果去添加其他类型的数据类型,就会报错.

?发现,此时就会报错,那么怎么来存放这样的一个数组呢?我们知道,在我们的所有的数据类型他们都统一来自于我们的object这个父类.那么我们是不是可以定义一个object数组,来存放不同的数据类型的元素呢?

此时我们就发现可以来定义这样的数组来存放不同类型的元素.

那么我们在在这给大家一段代码,让我们来存储数组的元素和打印数组的元素,

class MyArray{
    Object [] obj = new Object[10];//定义Object数组

    public void setObj(int sot,int val) {//在sot下标下存入val
        this.obj[sot] = val;
    }

    public Object getObj(int sot) {//访问sot下标的元素
        return this.obj[sot];
    }
}
public class test {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setObj(0,10);
        myArray.setObj(1,20);
        int ret = myArray.getObj(1);
        System.out.println(ret);
    }
}

?当我们写出这样的代码,我们定义一个Object数组,在0下标存10,在1下标存20,当我们调用get方法的时候,我们发现,此时的代码会报错,那么为什么会报错,明明我1下标下存放的就是int型的数据,我用int去接收不应该有错误呀,但是记得我们在定义get方法的时候,我们的返回类型是Object,所以,此时要想我们的代码没有错误就必须要强转为int?

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

3.泛型

3.1 泛型的语法

class 泛型类名称<类型形参列表> {
     // 这里可以使用类型参数
     }
class ClassName<T1, T2, ..., Tn> {
 }

3.2 泛型的引用

那么我们就可以去修改上面的代码.
class MyArray<T>{
    public  T[] obj =(T[]) new Object[10];//定义Object数组

    public void setObj(int sot,T val) {//在sot下标下存入val
        this.obj[sot] = val;
    }

    public T getObj(int sot) {//访问sot下标的元素
        return this.obj[sot];
    }
}
public class test {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>();
        myArray.setObj(0,10);
        myArray.setObj(1,20);
        Integer ret = myArray.getObj(0);
        System.out.println(ret);

        MyArray<String> myArray1 = new MyArray<>();
        myArray1.setObj(0,"hello");
        myArray1.setObj(1,"hahaha");
        String str = myArray1.getObj(0);
        System.out.println(str);

    }
}

?

此时发现不需要强转类型,这就是我们泛型的好处.

3.3 泛型的上界?

首先提出一个例子,在泛型类中有一个数组,让你去给这个数组按照从小到大的顺序去排列

class A <T> {
    public T findMax(T[] array){
        T max = array[0];//假设0下标的是最大值
        for (int i = 1; i < array.length ; i++) {
            if (max < array[i]){
                max = array[i];
            }
        }
        return max;
    }
}
public class Test2 {
    public static void main(String[] args) {
        A<Integer> a = new A<>();
        Integer [] array = {1,2,3,4};
        Integer ret = a.findMax(array);
        System.out.println(ret);
    }
}

当我写完这样的代码的时候,我发现我都是按照题目的要求去求解的,但是此时在判断的时候JVM报错了,那么为什么会报错,因为在我们的JVM中,我们不知道传入的参数类型是什么,不同的类型你怎么让JVM去给你比较.那么此时相信大多数人都想到了我们的compareTo,用它来实现,那么怎么去使用呢.

class A <T extends Comparable<T>> {
    public T findMax(T[] array){
        T max = array[0];//假设0下标的是最大值
        for (int i = 1; i < array.length ; i++) {
            if (max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }

}

?

?此时我们寻找最大的值就找到了.

class A <T extends Comparable<T>> {
    public T findMax(T[] array){
        T max = array[0];//假设0下标的是最大值
        for (int i = 1; i < array.length ; i++) {
            if (max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }

}

class Preson implements Comparable<Preson> {
    public int age;


    public Preson(int age){
        this.age =age;
    }

    @Override
    public String toString() {
        return "Preson{" +
                "age=" + age +
                '}';
    }


    @Override
    public int compareTo(Preson o) {
        return this.age - o.age;
    }
}
public class Test2 {
    public static void main(String[] args) {
        A<Preson> a = new A<>();
        Preson[] preson ={new Preson(10),new Preson(20)};
        Preson ps = a.findMax(preson);
        System.out.println(ps);
    }

?在这我们根据一个Person数组,通过年龄的大小来输出了年龄最大的.在这我们的person类继承了我们的Conparable接口,那么我们就可以在泛型类中传入我们的Person这种类型,此时我们的Person就是我们此时的泛型类的上界,也就是说A<类类型>,我们放进去的类类型就是我们的泛型类的上界.

泛型上界的语法方式:
class 泛型类名称 < 类型形参 extends 类型边界 > {
...
}

?

注意: 泛型没有下界.

4. 泛型方法

就是八把一个方法给他写成返回值是泛型的一个方法:

泛型方法的语法:

方法限定符 < 类型形参列表 > 返回值类型 方法名称 ( 形参列表 ) { ... }
在这给大家举一个例子:
class Preson implements Comparable<Preson> {
    public int age;


    public Preson(int age){
        this.age =age;
    }

    @Override
    public String toString() {
        return "Preson{" +
                "age=" + age +
                '}';
    }


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


class A2  {
    public static <T extends Comparable>T findMax(T[] array){
        T max = array[0];//假设0下标的是最大值
        for (int i = 1; i < array.length ; i++) {
            if (max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }

}
public class Test2 {
    public static void main(String[] args) {
    Preson[] presons = {new Preson(10),new Preson(20)};
    Preson ret = A2.findMax(presons);
        System.out.println(ret);
    }

在这,我是将我们的泛型方法直接转换成了静态方法.那么此时有人要问了那你传参的数据类型在哪呢,我只是看到你返回的类型是Person,在这做个解释

?5. 通配符

通配符是用来解决泛型无法协变的问题的 ,协变指的就是如果 Student Person 的子类,那么 List<Student> 也应 该是 List<Person> 的子类。但是泛型是不支持这样的父子类关系的。
泛型 T 是确定的类型,一旦你传了我就定下来了,而通配符则更为灵活或者说是不确定,更多的是用于扩充 参数的范围.
在这举个例子说明:
class Message<T> {
    private T message;

    public T getMessage() {
        return message;
    }

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

public class Test3 {
    public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setMessage("你好呀,朋友.");
        func(message);
    }
    public static void func(Message <String> ret){
        System.out.println(ret.getMessage());
    }
}

这样的代码是没有错误的,但是我们的message只能传String类型的,

?

那么我们怎么让它改正确呢?

方法一:

?

所以说这样的方法是不是很麻烦,一点都不实用,一旦我message的类型一变,你就要给我的func函数的接收类型也要变.

方法2:就是使用我们的通配符

 public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setMessage("你好呀朋友");
        func(message);
    }
    public static void func(Message <?> ret){
        System.out.println(ret.getMessage());
    }

?

?

?5.1通配符的上界

通配符上界的语法:

<? extends 上界 >
<? extends Number > // 可以传入的实参类型是 Number 或者 Number 的子类
举例说明:
class Food {

}
class Fruit extends Food {

}
class Apple extends Fruit {

}
class Banana extends Fruit {

}
class Message1<T>{
    private T message;

    public T getMessage() {
        return message;
    }

    public void setMessage(T message) {
        this.message = message;
    }
}
public class Test4 {
    public static void main(String[] args) {
        Message<Food> message = new Message<>();
        message.setMessage(new Food());
        func(message);
         
        Message<Apple> message1 = new Message<>();
        message1.setMessage(new Apple());
        func(message1);
    }
    public static void func(Message<? extends Food> ret){
        System.out.println(ret.getMessage());
    }
}

5.2 通配符的下界

语法:

<? super 下界 >
<? super Integer > // 代表 可以传入的实参的类型是 Integer 或者 Integer 的父类类型

?也是举个例子说明:

class Food1 {

}
class Fruit1 extends Food {

}
class Apple1 extends Fruit1 {

}
class Message2<T>{
    private T message;

    public T getMessage() {
        return message;
    }

    public void setMessage(T message) {
        this.message = message;
    }
}
public class Test5 {
    public static void main(String[] args) {
        Message2<Fruit1> message2 = new Message2<>();
        message2.setMessage(new Fruit1());
        func2(message2);

        Message2<Food1> message21 = new Message2<>();
        message21.setMessage(new Food1());
        func2(message2);

    }
    public static void func2(Message2<? super Fruit1> ret){
        ret.setMessage(new Apple1());
        ret.setMessage(new Fruit1());
        System.out.println(ret.getMessage());
    }
}

4. 拓展

public class Test6 {
    public static void main(String[] args) {
        int a = 100;
        int b = 100;
        System.out.println(a==b);

        Integer c = 200;
        Integer d = 200;
        System.out.println(c == d);
    }
}

运行结果:

?为什么一个是ture 一个是false?

这个问题留给兄弟们,可以去参考Integer的源码,一看便知.

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 21:19:25  更:2022-09-24 21:21:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 19:21:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码