Java泛型上界与泛型方法的应用 | 如何通过泛型类获取任意类型的三个数的最大值?

news2024/9/20 18:54:55

目录

一、引言

二、泛型上界

1、什么是泛型的上界

2、泛型上界的语法

三、泛型方法

1、泛型方法的语法

2、泛型方法的类型推导

三、编程分析

1、MyCompare泛型类

2、泛型方法实现

四、总结


一、引言

初学Java时,同学们基本都会遇到这样一个基础编程题:

实验题目:获取三个整数的最大值。

它的答案非常简单,只需要比较三个int类型的变量即可:

public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 30;
        int temp = (a > b) ? a : b;
        int max = (temp > c) ? temp : c;
        System.out.println("max = " + max);
    }
}

那么问题来了:如果想要比较的对象不仅是整数,而是任意的数值类型(甚至任意的自定义类型),那该怎么办呢?

诚然,我们可以通过方法的重载,编写多个适用于不同数据类型的方法。在需要时,我们可以挨个进行调用。但这个未免还是有些吃力,毕竟光是数值类型就有7种,而这7种类型都是可以比较大小的。

因此,我们可以采用泛型类,将数据类型也作为参数传递,达到比较“任意的数值类型”的效果。

这个方法主要用到了泛型的上界这一知识点。对于泛型的基本语法,便不再赘述,需要的同学可以再自行搜索、熟悉。


二、泛型上界

1、什么是泛型的上界

为什么要引出泛型的上界?因为在实际编程中,在定义泛型类时,有时需要对传入的类型变量做一定的约束。比如上面这个题目,约束就是“数值类型”,也就是Number类型。Integer、Double等类型(注意:泛型类型是引用类型,要传基本数据类型只能传包装类)都可以进行大小比较,但此时String类型就不在数值类型的范围内。这就是约束,约束传入的数据类型是哪些,而不是随便哪个数据类型都可以传进去。

我们可以通过类型边界来约束。

2、泛型上界的语法

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

例如:

public class Test<E extends Number> {
...
}

 <E extends Number>的含义就是,Test类只接受 Number 的子类(或Number类本身)作为 E 。如果没有指定E的上界(PS:泛型没有下届,泛型的边界就是指泛型是上界)。

因此:

Test<Integer> a; // 正确,因为 Integer 是 Number 的子类型
Test<String> b; // 编译错误,因为 String 不是 Number 的子类型

这样就达到了约束的效果。

但是这样还不够。如果要约束的上界不是一个类,而是一个接口,该如何呢?语法如下:

public class Test<E extends Comparable<E>> {
...
}

<E extends Comparable<E>>的含义是,传入的E必须实现了Comparable接口。


三、泛型方法

1、泛型方法的语法

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { 
    ... 
}

例如:

public class Util {
    //静态的泛型方法 需要在static后用<>声明泛型类型参数
    public static <E> void swap(E[] array, int i, int j) {
        E t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}

此时,Util不是泛型类,但swap是泛型方法。该方法将在《Java泛型上界与泛型方法的应用 | 如何给数组排序》一文中详细介绍。此处我们只需要知道泛型方法的语法即可。

2、泛型方法的类型推导

类型推导是指,在调用泛型方法时,可以不写明方法的类型,而Java会根据传入参数的类型自动推导出泛型方法中的泛型是指那种类型。如我们既可以这样写:

Integer[] a = { ... };
swap(a, 0, 9);
String[] b = { ... };
swap(b, 0, 9);
//没有写明swap传入的类型是什么,而是Java自动推导

也可以这样写:

Integer[] a = { ... };
Util.<Integer>swap(a, 0, 9);
String[] b = { ... };
Util.<String>swap(b, 0, 9);
//写明了swap方法传入的类型

有了以上的知识,就可以进行编程了。


三、编程分析

1、MyCompare<T>泛型类

public class MyCompare<T extends Comparable<T>>{
/*    public T getMax(T x, T y) {
        return x > y ? x : y;   //错误!
    }*/
    public T getMax(T x, T y) {
        return x.compareTo(y) >= 0 ? x : y;
    }
}

由于T本身代表的就是一个引用类型,因此,直接用大于小于号比较是不正确的。

不能写作:

调用compareTo()方法才是正确的比较方式。但compareTo()方法不是可以直接调用的,使用之前必须实现Comparable接口。因此,我们需要对传入的类型进行约束,令传入的类型一定得是实现了Comparable接口的,最后再调用类型的compareTo()方法进行比较。

因此,我们写作:

public class MyCompare<T extends Comparable<T>> {
    //获取较大值的方法
    ...
}

 含义为,MyCompare类只能传入实现过Comparable接口的类型T。

然后我们调用:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        double a = reader.nextDouble();
        double b = reader.nextDouble();
        double c = reader.nextDouble();

        MyCompare<Double> m = new MyCompare<>();
        double temp = m.getMax(a,b);
        double max = m.getMax(temp,c);
        System.out.println("max = " + max);
    }
}

此时传入了Double类型(Double类型是实现了Comparable接口的)。

 将Double改为Integer:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        int a = reader.nextInt();
        int b = reader.nextInt();
        int c = reader.nextInt();

        MyCompare<Integer> m = new MyCompare<>();
        int temp = m.getMax(a,b);
        int max = m.getMax(temp,c);
        System.out.println("max = " + max);
    }
}

 甚至可以传入String,因为String也实现了Comparable接口。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        String a = reader.next();
        String b = reader.next();
        String c = reader.next();

        MyCompare<String> m = new MyCompare<>();
        String temp = m.getMax(a,b);
        String max = m.getMax(temp,c);
        System.out.println("max = " + max);
    }
}

也可以传入自定义的类型Student,需要手动给Student类实现Comparable接口。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        Student stu1 = new Student(10);
        Student stu2 = new Student(20);
        Student stu3 = new Student(30);

        MyCompare<Student> m = new MyCompare<>();
        Student temp = m.getMax(stu1,stu2);
        Student max = m.getMax(temp,stu3);
        System.out.println("max = " + max);
    }
}

class Student implements Comparable<Student>{    //实现Comparable接口,接口也要传入泛型Student类
    int age;
    String name;
    double grades;

    public Student(int age) {    //构造方法 由于只用到age,就只写了age
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {    //重写compareTo方法
        return this.age-o.age;
    }

    @Override
    public String toString() {    //重写toString方法,便于打印
        return "Student{" +
                "age=" + age +
                '}';
    }
}


2、泛型方法实现

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        //创建待比较的对象
        String a = new String("hello");
        String b = new String("Hi");
        String c = new String("nice");

        //直接调用泛型方法
        //类型推导
        String temp = Util.getMax(a,b);
        String max = Util.getMax(temp,c);
        System.out.println("max = " + max);
    }
}

//泛型方法
class Util {
    public static <T extends Comparable> T getMax(T x,T y) {
        return x.compareTo(y) > 0 ? x : y;
    }
}


四、总结

  1. 泛型上界是对传入泛型类的类型变量做一定的约束,可以是约束类型变量必须是另一个类的子类或本身,也可以是约束类型变量必须实现了某一个接口。

  2. 泛型类型都是引用类型。要实现比较,传入的泛型类型必须是实现了Comparable接口的。语法为<T extends Comparable<T>>。通过调用引用变量自身的compareTo()方法,A.compareTo(B)这样来比较。

  3. 注意泛型的语法,包括泛型类实现接口、泛型方法的语法等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/142031.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

平面设计师去哪里找素材?

5个平面设计素材网站&#xff0c;赶紧收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/?vNTYwNDUx ​ 站内平面海报、UI设计、电商淘宝、免抠、高清图片、样机模板等素材非常齐全。还有在线抠图、CDR版本转换功能&#xff0c;能有效的为设计师节省找素材时间&#x…

MySQL查询训练题1

表信息&#xff1a; dept表和emp表 bonus表和salgrade表 练习题&#xff1a; 1、选择部门30中的所有员工&#xff1b; select * from Emp where DEPTNO30;2、列出所有办事员(CLERK)的姓名&#xff0c;编号和部门编号&#xff1b; select ENAME 姓名,EMPNO 编号,DEPTNO 部门…

【vue2中使用axios和插槽】一.组件的生命周期;二.vue2中使用axios;三.插槽

目录 一.组件的生命周期 1.组件的生命周期经历的阶段&#xff1a; &#xff08;1&#xff09;创建阶段&#xff1a;beforeCreate、created、beforeMount、mounted &#xff08;2&#xff09;运行阶段&#xff1a;beforeUpdate、update &#xff08;3&#xff09;销毁阶段&a…

C++ 学习 Day.10(标准模板库简介)

标准模板库&#xff1a; 简单地说&#xff0c;标准模板库&#xff08;STL&#xff09;是一组模板类和函数&#xff0c;向程序员提供了&#xff1a; • 用于存储信息的容器&#xff1b; • 用于访问容器存储的信息的迭代器&#xff1b; • 用于操作容器内容的算法。 关于STL可见…

Git学习和使用

目录&#xff1a;Git概念和知识Git的四个工作区域和工作流程Git 的四个工作区域Git 的工作流程git文件状态常用操作进阶操作单个本地库绑定多远程仓库方案Git概念和知识 Git的四个工作区域和工作流程 Git 的四个工作区域 Remote&#xff1a;远程仓库 位于托管代码的服务器&a…

【语义分割】语义分割综述文章

目录&#xff1a;语义分割一、什么是语义分割二、什么是图像中的语义信息&#xff1f;三、语义分割中的上下文信息四、语义分割方法五、语义分割神经网络六、目前比较经典的网络七、评价指标一、什么是语义分割 语义分割&#xff0c;也称为像素级分类问题&#xff0c;其输出和…

如何远程访问别人的MySQL数据库

1、 如何远程访问别人的MySQL数据库 - curryzwy - 博客园 (cnblogs.com)https://www.cnblogs.com/curryzwy/p/15730485.html 2、 mysql——同一局域网如何共同访问一台电脑的数据库&#xff08;胎教级教学&#xff09;_七月星辰的博客-CSDN博客_两台电脑共用一个mysql数据库…

1803. 统计异或值在范围内的数对有多少

解法一&#xff1a;字典树 前置知识&#xff1a;字典树 字典树是一种实现字符串快速检索的多叉树结构。 例如&#xff1a;给定字符串集合[cab, cos, car, cat], 我们现在需要判断cat是否存在于字符串集合中。 字典树代码&#xff1a; static int[][] trie new int[N][26]; …

AcWing 1221. 四平方和(二分或哈希)

一、题目描述 二、思路分析 先从时间复杂度的角度入手&#xff0c;这道题的数据范围是106&#xff0c;因此我们的时间复杂度要控制在O(n)O(n)O(n)或者O(nlogn)O(nlogn)O(nlogn)。 对于abcd中的任何一个元素&#xff0c;必定是小于n\sqrt nn​的。 我们的一个思路就是去枚举&…

如何选择用 .net Framework 或 .net core

小米问&#xff1a; 给你一个项目&#xff0c;如何选择用 netframework 或 netcore&#xff1f;如何选择服务器&#xff1f; 怎么去考虑&#xff1f; 咋回答呢 答&#xff1a; 不要考虑.net framework 除非极其特殊的情况 比如目标主机系统版本较低 服务器自然是linux好&a…

2023年考证时间一览表

2022年已经成为历史&#xff0c;在疫情背景全面开放下给大家整理了2023年全年的考试时间以及报名时间新鲜出炉&#xff0c;了解清楚&#xff0c;为2023年提前做好规划&#xff01; 1月份 2022年下半年中小学教师资格考试面试 报名时间&#xff1a;2022年12月9日-12日 考试时间…

大数据:Hive视图和索引

一、视图 1.1 简介 Hive 中的视图和 RDBMS 中视图的概念一致&#xff0c;都是一组数据的逻辑表示&#xff0c;本质上就是一条 SELECT 语句的结果集。视图是纯粹的逻辑对象&#xff0c;没有关联的存储 (Hive 3.0.0 引入的物化视图除外)&#xff0c;当查询引用视图时&#xff0…

【自学C++】Windows安装C++语言开发环境

Windows安装C语言开发环境 Windows安装C语言开发环境教程 C 的开发环境可以直接使用 C 语言 的开发环境&#xff0c; 同时&#xff0c;Windows 本身就自带 C 语言的运行环境&#xff0c;因此&#xff0c;为了开发 C 语言&#xff0c;我们只需要安装一个 C 语言的开发工具即可…

第03讲:HTTP操作之ElasticSearch文档操作

3.1.2、文档操作 实验1&#xff1a;创建文档 索引已经创建好了&#xff0c;接下来我们来创建文档&#xff0c;并添加数据。这里的文档可以类比为关系型数据库中的表数据&#xff0c;添加的数据格式为 JSON 格式 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 :http://1…

【Kafka】Java实现数据的生产和消费

【Kafka】Java实现数据的生产和消费 Kafka介绍 Kafka 是由 Linkedin 公司开发的&#xff0c;它是一个分布式的&#xff0c;支持多分区、多副本&#xff0c;基于 Zookeeper 的分布式消息流平台&#xff0c;它同时也是一款开源的基于发布订阅模式的消息引擎系统。 Kafka 有如下…

【LeetCode】1803. 统计异或值在范围内的数对有多少

1803. 统计异或值在范围内的数对有多少 题目描述 给你一个整数数组 nums &#xff08;下标 从 0 开始 计数&#xff09;以及两个整数&#xff1a;low 和 high &#xff0c;请返回 漂亮数对 的数目。 漂亮数对 是一个形如 (i, j) 的数对&#xff0c;其中 0 < i < j <…

阻塞式队列

文章目录一、阻塞队列阻塞队列的概念阻塞队列相关类和方法生产者消费者模型二、自定义实现阻塞队列自定义实现循环队列自定义实现阻塞队列生产者消费者模型体验一、阻塞队列 阻塞队列的概念 队列我们并不默认&#xff0c;一提起队列&#xff0c;我们立马就能想到 "先进先…

一条 update 语句的执行过程

1.一条 update 语句的执行流程 一条更新语句&#xff0c;其实是增&#xff0c;删&#xff0c;查的综合体&#xff0c;查询语句需要经过的流程&#xff0c;更新语句全部需要执行一次&#xff0c;因为更新之前必须要先拿到&#xff08;查询&#xff09;需要更新的数据。 Buffer…

低代码平台组件间通信方案复盘

背景介绍3年前我开发了一款零代码搭建平台 H5-Dooring, 主要目的是想用更低的成本, 更快的效率, 上线 web 页面(其实是不想写重复的代码了,写麻了). 好在陆陆续续折腾了3年, 目前已经可以满足基本的页面设计和搭建能力, 并能快速上线页面.之前也在社区分享了很多低代码和零代码…

高、低成本MEMS惯导系统姿态、位置、速度更新算法的对比

高、低成本MEMS惯导系统姿态、位置、速度更新算法的对比一、高成本MEMS惯导系统姿态、位置、速度更新算法1、速度更新2、位置更新3、姿态更新4、程序仿真及实验结果4.1 主函数4.2 子函数4.3 实验结果一、低成本MEMS惯导系统姿态、位置、速度更新算法1、速度更新2、位置更新3、姿…