Java学习第八部分——泛型

目录

一、概述

(一)定义

(二)作用

(三)引入原因

二、使用

(一)类

(二)接口

(三)方法

三、类型参数

(一)命名

(二)限制

四、类型擦除

(一)概念

(二)影响

五、通配符

(一)`?`(无界通配符)

(二)`? extends T`(上界通配符)

(三)`? super T`(下界通配符)

六、高级用法

(一)泛型数组

(二)泛型与反射

(三)泛型与匿名内部类

七、idea项目实战

(一)打开idea新建Java项目

(二)编写GenericCollectionUtil类

(三)创建测试类

(四)编译运行代码,注意切换到测试类文件

(五)运行结果如下

(六)代码解释如下

1. GenericCollectionUtil 类

2. GenericCollectionUtilTest`类


一、概述

(一)定义


        Java泛型(Generics)是指在定义类、接口或方法时,可以指定一个或多个类型参数(Type Parameters),这些类型参数在使用时会被具体的类型替换。它允许程序员在编写代码时延迟确定数据类型,从而提高代码的复用性和安全性。

(二)作用

       Java泛型是一种强大的特性,它可以让代码更加通用、安全和复用。通过泛型类、泛型接口和泛型方法,可以编写出能够处理多种类型数据的代码。同时,泛型的类型参数、类型擦除和通配符等概念也需要深入理解,以便正确使用泛型。

(三)引入原因


1. 类型安全
   - 在没有泛型的情况下,集合类(如ArrayList)只能存储Object类型的对象。当从集合中取出元素时,需要进行强制类型转换。例如:

     List list = new ArrayList();
     list.add("Hello");
     String str = (String) list.get(0);

     这种方式存在类型安全问题,因为list可以存储任何类型的对象,如果添加了其他类型的对象,如`list.add(123);`,在取出时进行强制类型转换就会抛出`ClassCastException`异常。
   - 使用泛型后,可以在编译阶段就检查类型是否正确。例如:

     List<String> list = new ArrayList<String>();
     list.add("Hello");
     String str = list.get(0); // 不需要强制类型转换

     如果尝试添加非String类型的对象,如`list.add(123);`,编译器会报错。
2. 代码复用
   - 泛型可以让我们编写更加通用的代码。例如,一个泛型类可以处理多种类型的数据,而不需要为每种数据类型都编写一个类。

二、使用

(一)类


1. 定义泛型类
   - 泛型类的定义格式为:

     class 类名<类型参数> {
         // 类的成员可以使用类型参数
     }

   - 例如,定义一个简单的泛型类Box:

     public class Box<T> {
         private T t;

         public void set(T t) {
             this.t = t;
         }

         public T get() {
             return t;
         }
     }

2. 使用泛型类
   - 使用时需要指定具体的类型参数。例如:

     Box<String> stringBox = new Box<String>();
     stringBox.set("Hello");
     String str = stringBox.get();

     Box<Integer> integerBox = new Box<Integer>();
     integerBox.set(123);
     Integer num = integerBox.get();
 

(二)接口


1. 定义泛型接口
   - 泛型接口的定义格式与泛型类类似:
        interface 接口名<类型参数> {
         // 接口的成员可以使用类型参数
     }

   - 例如,定义一个泛型接口Comparable:
 
     public interface Comparable<T> {
         int compareTo(T o);
     }

2. 实现泛型接口
   - 实现泛型接口时,可以指定具体的类型参数,也可以不指定。例如:
  
     public class Person implements Comparable<Person> {
         private String name;

         public Person(String name) {
             this.name = name;
         }

         @Override
         public int compareTo(Person o) {
             return this.name.compareTo(o.name);
         }
     }
 

(三)方法


1. 定义泛型方法
   - 泛型方法的定义格式为:
 
     <类型参数> 返回值类型 方法名(参数列表) {
         // 方法体
     }

   - 例如,定义一个泛型方法swap:

     public <T> void swap(T[] array, int i, int j) {
         T temp = array[i];
         array[i] = array[j];
         array[j] = temp;
     }

2. 使用泛型方法
   - 调用泛型方法时,可以省略类型参数,编译器会自动推断。例如:

     String[] strArray = {"a", "b"};
     swap(strArray, 0, 1);
 

三、类型参数

(一)命名


- 通常使用单个大写字母作为类型参数的名称,常见的有:
  - `T`:Type
  - `E`:Element
  - `K`:Key
  - `V`:Value
  - `N`:Number

(二)限制


1. 有界类型参数
   - 可以通过`extends`关键字对类型参数进行限制,使其只能是某个类或接口的子类型。例如:

     public <T extends Number> void print(T t) {
         System.out.println(t);
     }

     这样,`T`只能是`Number`或其子类的实例。
2. 无界类型参数
   - 如果没有对类型参数进行限制,那么它就是一个无界类型参数,可以是任何类型。例如:

     public <T> void print(T t) {
         System.out.println(t);
     }
 

四、类型擦除

(一)概念


- 在Java中,泛型是通过类型擦除实现的。也就是说,在运行时,泛型类型会被擦除,所有的类型参数都会被替换为它们的边界(如果有边界的话),如果没有边界,则替换为`Object`。
- 例如,`List<String>`和`List<Integer>`在运行时都会被擦除为`List`。

(二)影响


1.无法获取泛型类型参数
   - 由于类型擦除,无法在运行时获取泛型类型参数的具体类型。例如:

     List<String> list = new ArrayList<String>();
     System.out.println(list.getClass().getTypeParameters()); // 输出的是一个空的TypeVariable数组

2.泛型方法的重载
   - 由于类型擦除,泛型方法的签名在运行时是相同的,因此不能仅根据类型参数的不同来重载泛型方法。例如:
  
     public <T> void print(T t) {
         System.out.println(t);
     }

     public <E> void print(E e) {
         System.out.println(e);
     }

     这是不允许的,因为编译器无法区分这两个方法。

五、通配符

(一)`?`(无界通配符)


- 表示任意类型。例如:

  public void printList(List<?> list) {
      for (Object obj : list) {
          System.out.println(obj);
      }
  }

  这个方法可以接受任何类型的`List`作为参数。

(二)`? extends T`(上界通配符)


- 表示类型参数是`T`或`T`的子类型。例如:

  public void printNumberList(List<? extends Number> list) {
      for (Number num : list) {
          System.out.println(num);
      }
  }

  这个方法可以接受`List<Number>`、`List<Integer>`、`List<Double>`等作为参数。

(三)`? super T`(下界通配符)


- 表示类型参数是`T`或`T`的父类型。例如:

  public void addNumberToList(List<? super Integer> list) {
      list.add(123);
  }

  这个方法可以接受`List<Integer>`、`List<Number>`、`List<Object>`等作为参数。

六、高级用法

(一)泛型数组


- 不能创建泛型数组,例如`new T[10]`是不允许的。但是可以通过其他方式来实现类似的功能。例如:

  public <T> T[] createArray(Class<T> clazz, int size) {
      return (T[]) Array.newInstance(clazz, size);
  }
 

(二)泛型与反射


- 可以通过反射来获取泛型类型参数。例如:
 
  Type type = new TypeReference<List<String>>() {}.getType();
  if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
      System.out.println(actualTypeArguments[0]); // 输出:class java.lang.String
  }
 

(三)泛型与匿名内部类


- 可以在匿名内部类中使用泛型。例如:

  public static void main(String[] args) {
      List<String> list = new ArrayList<String>() {
          {
              add("Hello");
          }
      };
      System.out.println(list.get(0));
  }
 

七、idea项目实战

(一)打开idea新建Java项目

ps:如果需要,可以在pom.xml文件中添加其他依赖。对于这个简单的项目不需要额外的依赖。

(二)编写GenericCollectionUtil

package com.example.util;import java.util.ArrayList;
import java.util.List;public class GenericCollectionUtil<T> {private List<T> list = new ArrayList<>();/*** 添加元素到集合** @param element 要添加的元素*/public void add(T element) {list.add(element);}/*** 获取集合中的元素** @param index 元素的索引* @return 索引对应的元素*/public T get(int index) {return list.get(index);}/*** 清空集合*/public void clear() {list.clear();}/*** 获取集合大小** @return 集合的大小*/public int size() {return list.size();}@Overridepublic String toString() {return list.toString();}
}

(三)创建测试类

package com.example.util;public class GenericCollectionUtilTest {public static void main(String[] args) {// 测试字符串集合GenericCollectionUtil<String> stringUtil = new GenericCollectionUtil<>();stringUtil.add("Hello");stringUtil.add("World");System.out.println("String Collection: " + stringUtil);System.out.println("Element at index 0: " + stringUtil.get(0));stringUtil.clear();System.out.println("After clear: " + stringUtil);// 测试整数集合GenericCollectionUtil<Integer> intUtil = new GenericCollectionUtil<>();intUtil.add(1);intUtil.add(2);intUtil.add(3);System.out.println("Integer Collection: " + intUtil);System.out.println("Element at index 1: " + intUtil.get(1));intUtil.clear();System.out.println("After clear: " + intUtil);}
}

(四)编译运行代码,注意切换到测试类文件

ps:红色箭头所示两种点击都可以实现

(五)运行结果如下

(六)代码解释如下

1. GenericCollectionUtil 类

       这个类是一个泛型工具类,用于处理不同类型的数据集合操作。它使用了 Java 的泛型特性,使得同一个类可以处理多种类型的数据。

成员变量
- `private List<T> list = new ArrayList<>();`
  - 这是一个泛型列表,用于存储集合中的元素。`T` 是一个类型参数,表示集合中元素的类型。

方法
- `public void add(T element)`
  - 这是一个泛型方法,用于向集合中添加元素。`element` 是要添加的元素,其类型由 `T` 决定。
  
- `public T get(int index)`
  - 这是一个泛型方法,用于从集合中获取指定索引位置的元素。返回值的类型也是 `T`。
  
- `public void clear()`
  - 这是一个非泛型方法,用于清空集合中的所有元素。
  
- `public int size()`
  - 这是一个非泛型方法,用于获取集合中元素的数量。
  
- `@Override public String toString()`
  - 这是一个重写的方法,用于返回集合的字符串表示形式。它直接返回 `list.toString()`,即集合中所有元素的字符串表示。

2. GenericCollectionUtilTest`类

这个类是一个测试类,用于验证 `GenericCollectionUtil` 类的功能。

主要逻辑
- 创建 `GenericCollectionUtil` 类的实例,分别用于字符串和整数类型的集合。
- 向集合中添加元素,并打印集合的字符串表示形式。
- 获取并打印指定索引位置的元素。
- 清空集合,并打印清空后的集合表示形式。

输出示例
- 打印字符串集合和整数集合的初始状态。
- 打印指定索引位置的元素。
- 打印清空集合后的状态。

优点
- **泛型使用**:通过使用泛型,代码更加通用和灵活,可以处理多种类型的数据集合。
- **简洁性**:代码简洁明了,易于理解和维护。
- **功能明确**:每个方法的功能都很明确,易于使用。

缺点
- **功能有限**:目前只实现了添加元素、获取元素、清空集合和获取集合大小的功能,还可以扩展更多功能,如删除元素、查找元素等。
- **异常处理**:在获取元素时,如果索引超出范围,会抛出 `IndexOutOfBoundsException` 异常。可以考虑添加异常处理逻辑,提高代码的健壮性。

扩展功能建议
- **删除元素**:添加一个方法,用于从集合中删除指定索引位置的元素。
- **查找元素**:添加一个方法,用于查找集合中是否存在指定的元素。
- **排序**:添加一个方法,用于对集合中的元素进行排序。
- **过滤**:添加一个方法,用于对集合中的元素进行过滤,返回满足条件的元素集合。

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

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

相关文章

定时点击二次鼠标 定时点击鼠标

定时点击二次鼠标 定时点击鼠标 今天分享一个定时点击两次的小工具。 我们在生活中&#xff0c;可能会遇到一些定时点击的任务。比如说在晚上9点去发送一个群发&#xff0c;或者倒计时点击一个按钮。那么可以使用这个工具&#xff0c;仅适用于Windows电脑。 #定时点击鼠标 #倒计…

Linux网络配置与故障排除完全指南

1. ifconfig命令 - 网络接口配置器 ifconfig&#xff08;interface configurator&#xff09;是Linux系统中最基础的网络配置工具。该命令可以初始化网络接口、分配IP地址、启用或禁用接口&#xff0c;同时还能查看接口的详细信息。 查看网络接口信息 # ifconfig eth0 …

Python Pytest-Benchmark详解:精准性能测试的利器

在软件开发的迭代过程中&#xff0c;性能优化如同精密手术&#xff0c;需要精准的测量工具。Pytest-Benchmark作为pytest生态中的性能测试插件&#xff0c;凭借其无缝集成能力和专业统计功能&#xff0c;成为Python开发者进行基准测试的首选工具。本文将深入解析其技术特性与实…

60天python训练营打卡day51

学习目标&#xff1a; 60天python训练营打卡 学习内容&#xff1a; DAY 51 复习日 作业&#xff1a;day43的时候我们安排大家对自己找的数据集用简单cnn训练&#xff0c;现在可以尝试下借助这几天的知识来实现精度的进一步提高 学习时间&#xff1a; 2025.07.04 浙大疏锦行…

支持向量机(SVM)在肺部CT图像分类(肺癌检测)中的实现与优化

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…

YOLOv3-SPP 深度解析:引入 SPP 结构,显著提升目标检测性能!

✅ YOLOv3-SPP 技术详解 一、前言 YOLOv3-SPP 是在 YOLOv3 基础上加入 SPP&#xff08;Spatial Pyramid Pooling&#xff09;模块的一种改进版本&#xff0c;旨在提升模型对不同尺度目标的识别能力&#xff0c;尤其是在大目标检测方面表现更优。 它由 Alexey Bochkovskiy 在…

负载均衡--常见负载均衡算法

负载均衡算法可以分为两类&#xff1a;静态负载均衡算法和动态负载均衡算法。 1、静态负载均衡算法包括&#xff1a;轮询&#xff0c;比率&#xff0c;优先权 轮询&#xff08;Round Robin&#xff09;&#xff1a;顺序循环将请求一次顺序循环地连接每个服务器。当其中某个服务…

深入解析GCC:开源的编译器之王

在编程世界中&#xff0c;编译器是将人类可读代码转化为机器指令的关键桥梁。而GCC&#xff08;GNU Compiler Collection&#xff09; 无疑是这个领域最耀眼的明星之一。作为开源世界的基石&#xff0c;它支撑着Linux内核、众多开源项目和商业软件的构建。今天&#xff0c;我们…

https和http有什么区别

目录 一、核心区别&#xff1a;是否基于加密传输 二、底层传输机制差异 三、HTTPS 的加密原理 四、应用场景差异 五、其他细节区别 总结 在网络通信中&#xff0c;HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09; 和HTTPS&#xff0…

CSS3 文本效果详解

CSS3 文本效果详解 引言 随着Web技术的发展,CSS3为前端设计师和开发者提供了丰富的文本效果选项。这些效果不仅能够增强网页的美观性,还能提升用户体验。本文将详细介绍CSS3中的文本效果,包括文本阴影、文本描边、文本装饰、文本换行、文本大小写等,并探讨如何在实际项目…

MySQL 中 -> 和 ->> 操作符的区别

简介 MySQL 5.7 或更高版本&#xff0c;可以使用 ->> 和 -> 运算符简化语法这两个操作符都是用于提取 JSON 数据的&#xff0c;但有一些重要区别 -> 操作符 功能&#xff1a;提取 JSON 对象的指定路径的值 返回类型&#xff1a;返回 JSON 类型的值&#xff08;可…

Vue2 day07

1.vuex的基本认知2.构建多组件共享的数据环境步骤&#xff1a;1.在自己创建的文件夹下创建脚手架2.创建三个组件### 源代码如下App.vue在入口组件中引入 Son1 和 Son2 这两个子组件html <template><div id"app"><h1>根组件</h1><input ty…

简述MCP的原理-AI时代的USB接口

1 简介随着AI的不断发展&#xff0c;RAG&#xff08;检索增强生成&#xff09;和function calling等技术的出现&#xff0c;使得大语言模型的对话生成能力得到了增强。然而&#xff0c;function calling的实现逻辑比较复杂&#xff0c;一个简单的工具调用和实现方式需要针对不同…

CISSP知识点汇总-资产安全

CISSP知识点汇总 域1---安全与风险管理域2---资产安全域3---安全工程域4---通信与网络安全域5---访问控制域6---安全评估与测试域7---安全运营域8---应用安全开发域2 资产安全 一、资产识别和分类 1、信息分级(Classification): 按照敏感程度(机密性被破坏) 按照重要程度…

Spring Boot 3.x 整合 Swagger(springdoc-openapi)实现接口文档

本文介绍 Spring Boot 3.x 如何使用 springdoc-openapi 实现 Swagger 接口文档&#xff0c;包括版本兼容表、最简单的配置示例和常见错误解决方案。1. Spring Boot 3.x 和 springdoc-openapi 版本对应表Spring Boot 版本Spring Framework 版本推荐的 springdoc-openapi 版本3.0…

Redis内存队列Stream

本文为个人学习笔记整理&#xff0c;仅供交流参考&#xff0c;非专业教学资料&#xff0c;内容请自行甄别 文章目录概述一、生产者端操作二、消费者端操作三、消费组操作四、状态查询操作五、确认消息六、消息队列的选择概述 Stream是Redis5.0推出的支持多播的可持久化的消息队…

Minio安装配置,桶权限设置,nginx代理 https minio

**起因&#xff1a;因为用到ruoyi-vue-plus框架中遇到生产环境是https&#xff0c;但是http的minio上传的文件不能在后台系统中访问**安装配置minio1. 下载安装2. 赋文件执行权限3.创建配置文件4.创建minio.service新版minio创建桶需要配置桶权限1.下载客户端2.设置访问权限3.连…

数论基础知识和模板

质数筛 用于快速处理 1&#xff5e;n 中所有素数的算法 因为依次遍历判断每一个数是否质数太慢&#xff0c;所以把一些明显不能质数的筛出来 普通筛法&#xff0c;对于每个整数&#xff0c;删除掉其倍数。 bool vis[N];//0表示是质数 int pri[N],o; //质数表 void get(int n…

Ubuntu20.04.6桌面版系统盘制作与安装

概述 本教程讲述Ubuntu20.04.6桌面版的系统U盘制作与安装&#xff0c;所需工具为一台电脑、大于4G的U盘、一个需要安装Ubuntu系统的主机。 步骤1&#xff1a;下载系统镜像与rufus 在ubuntu官网下载 ubuntu-20.04.6-desktop-amd64.iso&#xff0c;如图 下载rufus工具&#xf…

【C++复习3】类和对象

1.3.1.简述一下什么是面向对象回答&#xff1a;1. 面向对象是一种编程思想&#xff0c;把一切东西看成是一个个对象&#xff0c;比如人、耳机、鼠标、水杯等&#xff0c;他们各 自都有属性&#xff0c;比如&#xff1a;耳机是白色的&#xff0c;鼠标是黑色的&#xff0c;水杯是…