码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Java 泛型


    一、泛型简介

    Java 泛型(generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。下面给一个 Java 泛型的简单例子:

    1. public static void printArray(E[] inputArray){ // 这里的 E 可代表任意类型
    2. for (E element: inputArray){ // 输出数组中的元素,无论是什么类型
    3. System.out.println(element);
    4. }
    5. }

    不仅仅在 Java 中有泛型的概念,在 C++ 中也有类似的概念,叫做模板。当然,一般来说只有静态语言才说有类似于泛型的语法,其实不然,对于动态语言也有类似的语法,但用途不太相同,如 Python3.12 中的新类型提示语法,类型形参语法,就有点像仿 Java 泛型语法而成的类型提示语法。

    下面给几个例子以进行对比学习:

    C++ 模板编程

    1. template<typename T> // 这里的 T 与 Java 泛型中的 T 类似
    2. void swap(T& a, T& b) {
    3. T temp = a;
    4. a = b;
    5. b = temp;
    6. }
    7. // C++ 的“泛型”有其独特性,语法与 Java 相比不太一样,毕竟 C++ 中这称之为模板

    Python 类型形参语法

    1. def print_array[T](array: list[T]) -> None: # 此处的 T 只是为了类型提示,无实际功能,可认为是特殊的注释
    2. """ Type Parameter Syntax """
    3. print(*array)
    4. # Java 用的是尖括号("<>")表示,而 Python 则是用方括号("[]")表示

    二、泛型语法

    所有的泛型声明都是一对尖括号("<>")加上泛型标记符以及范围限定的关键字,多个标记符之间用逗号隔开。

    2.1 泛型标记符

    Java 的泛型标记符都是约定俗成的,没有说强制某一种泛型标记符表示什么含义,只是按照约定俗成的来可以让其他人更容易理解你的代码。

    下面是一些常见的泛型标记符:

    标记符约定全称描述
    EElement在集合中使用,因为集合中的都是元素(element)
    TType表示任意 Java 的引用类型
    KKey在字典中使用,表示键(key)
    VValue在字典中使用,表示值(value)
    NNumber表示数字(number)类型
    UUnbounded无限制类型通配符,常用于泛型方法和泛型类的定义中
    ??无限制类型通配符,常用于泛型方法的返回类型声明和方法参数中

    另外,这里强调一点,上面的常用标记符只是约定俗成的,标记符并没有强制只能是一个字符,多个字符也是可以的,比如 abc,不过标记符还是要满足类型的写法,毕竟其本质也只是表示类型而已,因此一般使用大写开头。

    下面是一个简单的示例:

    1. public static void print(K key, V value){ // 这里举的例子是:泛型方法参数
    2. System.out.println(key + " : " + value);
    3. }

    当然,System.out.println 本身就可以打印很多类型的对象,这里只是举个例子,来介绍泛型的用法。

    2.2 限定泛型的范围

    在泛型的声明中可以使用 extends 和 super 关键字来限定泛型的范围,使其更符合我们预期的要求,含义与平时使用时略有差异, 下面列出一个表格以体现具体的差异:

    关键字(限定词)使用方法描述
    extends限定泛型 T 的上界,即 T 必须是类 someType 的子类
    super限定泛型 T 的下界,即 T 必须是类 someType 的父类

    特别说明,上表中 someType 可以是类(class),也可以是接口(interface)。

    下面给出一些具体的示例来详细地说明它们的用法:

    1. import java.util.ArrayList;
    2. public class Test {
    3. // extends Number 限定数组里面只能是数字
    4. public static extends Number> void printNumberArray(ArrayList arrayList) {
    5. for (N n: arrayList) System.out.println(n);
    6. }
    7. public static void main(String[] args) {
    8. ArrayList integerArrayList = new ArrayList<>();
    9. ArrayList stringArrayList = new ArrayList<>();
    10. integerArrayList.add(1);
    11. integerArrayList.add(2);
    12. printNumberArray(integerArrayList); // 正常运行
    13. stringArrayList.add("1");
    14. stringArrayList.add("2");
    15. printNumberArray(stringArrayList); // 类型不符合泛型范围,报错!
    16. }
    17. }

    三、泛型方法

    泛型用在方法中,可以表示此方法的参数是泛型的,也可以是此方法的返回类型是泛型的。

    3.1 泛型参数类型

    泛型的参数类型很好写,泛型的声明放在方法的返回类型之前,修饰符之后,声明之后就可以在参数中使用泛型了。

    语法:修饰符 泛型声明 返回类型 方法名 参数列表

    3.2 泛型返回类型

    泛型返回类型和泛型参数类型类似,泛型声明在方法的返回类型之前,修饰符之后,声明之后就可以在返回类型中直接使用泛型了。语法和上述一致。

    下面是一个简单的示例:

    1. public static extends Comparable> T maxNumber (T a, T b) { // 泛型方法
    2. return a.compareTo(b) > 0 ? a : b; // 返回较大值
    3. }

    四、泛型类(接口)

    泛型类中泛型声明与泛型方法非常类似,但又略有不同。其泛型声明在类名的后面。

    语法:修饰符 关键字 类(接口)名 泛型声明

    下面是一个具体示例:

    1. class TypeBox { // 泛型类
    2. public T type; // 泛型属性
    3. TypeBox (T type){
    4. this.type = type;
    5. }
    6. public void set(T value){
    7. type = value;
    8. }
    9. public T get(){
    10. return type;
    11. }
    12. }
    13. public class Test {
    14. public static void main(String[] args) {
    15. TypeBox typeBox_1 = new TypeBox(666); // 整数:OK
    16. TypeBox typeBox_2 = new TypeBox("Java"); // 字符串:OK
    17. System.out.println(typeBox_1.get()); // Output: 666
    18. System.out.println(typeBox_2.get()); // Output: Java
    19. }
    20. }

    五、类型擦除

    类型擦除是 Java 泛型中的一类特殊的机制,它出现的目的是为了兼容 JDK 5 之前的代码。

    类型擦除是指,在 Java 运行时,Java 会把所有的泛型都替换为它们最顶级的上界,也就是 Object 对象。比如 ArrayList 将被替换为 ArrayList,即 ArrayList。

    在 JDK 5 之前,是没有泛型的,当时的程序员们为了实现类似泛型的功能,是用 Object 对象代替完成的,但 Object 对象实现的“泛型”并不完善,无法真正地做到和 JDK 5 出现的泛型一样,但由于这种做法已经非常普遍了,为了兼容这种早期做法,Java 泛型就有了类型擦除这种机制,为的就是兼容旧代码。这种机制也是 Java 泛型和其他编程语言泛型的区别之一。

    顺便提一下,用 Object 对象实现类似泛型的功能:类型都用 Object 来定义,然后在运行时进行检查。

    这里有个细节需要注意一下:既然有类型擦除机制,会在运行时将泛型擦除掉,全部用 Object 替换,那为什么泛型机制还可以现在泛型类型变量的值?比如,ArrayList 为什么不能 add 一个 String 类型的数据?其实,并非不行,只是只能在运行时进行 add!运行时怎么 add 呢?这就要涉及到反射的知识了!反射知识详见:Java 反射_小康2022的博客-CSDN博客

    下面是一个示例:

    1. ArrayList arrayList = new ArrayList();
    2. arrayList.add(666);
    3. arrayList.add("Java"); // 此处无法直接 add,会报错
    4. Classextends ArrayList> arrayListClass = arrayList.getClass();
    5. Method add = arrayListClass.getDeclaredMethod("add", Object.class);
    6. add.invoke(arrayList, "Java"); // 通过反射机制可以在类型擦除机制之后成功 add
  • 相关阅读:
    计算机视觉系列-AlexNet论文复现学习笔记(一)
    C++ - 智能指针 - auto_ptr - unique_ptr - std::shared_ptr - weak_ptr
    单片机第二季:温度传感器DS18B20
    「TypeScript实用篇」配置了拦截器但axios返回值提示却没变?
    算法与数据结构(第一周)——线性查找法
    leaflet 绘制显示半圆形,扇形示例 (134)
    智能运维,为新型数据中心注入科技动能
    跳出以人为中心,从事情发展的角度看问题本质
    【C++】函数重载
    Spring相关源码解读
  • 原文地址:https://blog.csdn.net/weixin_62651706/article/details/132910673
    • 最新文章
    • 攻防演习之三天拿下官网站群
      数据安全治理学习——前期安全规划和安全管理体系建设
      企业安全 | 企业内一次钓鱼演练准备过程
      内网渗透测试 | Kerberos协议及其部分攻击手法
      0day的产生 | 不懂代码的"代码审计"
      安装scrcpy-client模块av模块异常,环境问题解决方案
      leetcode hot100【LeetCode 279. 完全平方数】java实现
      OpenWrt下安装Mosquitto
      AnatoMask论文汇总
      【AI日记】24.11.01 LangChain、openai api和github copilot
    • 热门文章
    • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
      奉劝各位学弟学妹们,该打造你的技术影响力了!
      五年了,我在 CSDN 的两个一百万。
      Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
      面试官都震惊,你这网络基础可以啊!
      你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
      心情不好的时候,用 Python 画棵樱花树送给自己吧
      通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
      13 万字 C 语言从入门到精通保姆级教程2021 年版
      10行代码集2000张美女图,Python爬虫120例,再上征途
    Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
    正则表达式工具 cron表达式工具 密码生成工具

    京公网安备 11010502049817号