当前位置: 首页 > news >正文

【Java基础】泛型

文章目录

  • 泛型
  • 一、概述
  • 二、泛型的使用
    • 1、类
    • 2、方法
    • 3、接口
  • 三、泛型通配符
    • 1、<?>
    • 2、<? extends T>
    • 3、<? super T>
  • 四、泛型的擦除
    • 1、泛型的擦除
    • 2、泛型边界的擦除
    • 3、无法实例化泛型类型

泛型

一、概述

泛型(Generic)是一种机制,允许你编写与数据类型无关的代码,增加代码的灵活性和可重用性。

  • 泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。

泛型的作用:

  1. 安全性:编译时检查类型,将运行时期的ClassCastException,转移到编译时期的编译失败。
  2. 灵活性:使类型参数化,可以预先地使用未知的类型,让设计的代码更通用灵活。
  3. 重用性:一个泛型类或方法可以处理多种数据类型,减少代码重复。
  4. 维护性:泛型代码通常更清晰,容易理解和维护。

注意事项:

  • 泛型只能在编译阶段起作用,到了运行阶段就会被擦除
  • 泛型只能是引用数据类型,不能是 基本数据类型。
  • 泛型在使用时指定实际的类型,如果不指定默认为Object类型。

二、泛型的使用

1、类

定义:类名之后

// 泛型一般用大写的单个字母表示,可以定义多个泛型,使用逗号分隔。
修饰符 class 类名<A, B, C> {}

使用:在类中使用,可以作为 实例方法 的 参数 和 返回值(静态方法不支持)

class Example<T> {// 作为实例方法的参数类型public void show(T t) {System.out.println(t);}// 作为实例方法的返回值类型public T get(int index) {return null;}// 静态方法 不能使用 类上定义的泛型// public static void test1(T t) {...}// public static T test2(int index) {...}
}

指定类型:创建对象时

public class Test {public static void main(String[] args) {// 在创建对象时,根据需要指定泛型的类型DataShow<String> ds = new DataShow<>();ds.show("Hello");  }
}

2、方法

定义:返回值之前

修饰符 <T> 返回值类型 方法名(T t) {}

使用:在方法内部使用

class Example {public static <T> void show(T t) {// 在方法内部使用System.out.println("t = " + t);}// 不同方法的泛型名称可以一致public static <T> void show2(T t) {}
}

指定类型:调用方法时,根据传参的类型

public class Test {public static void main(String[] args) {show(100);		// T -> Integershow("Hello");	// T -> String}
}

3、接口

定义:接口名之后

public interface 接口名<E> {}

使用:作为 接口方法 的 参数 或 返回值

public interface Example<T> {void method1(T t);T method2();
}

指定类型:

1、定义接口的实现类时,确定泛型的类型

public class ExampleChild1 implements Example<String>{@Overridepublic void method1(String s) {System.out.println(s);}@Overridepublic String method2() {return null;}
}

2、定义接口的实现类时,继续沿用泛型

public class ExampleChild2<T> implements Example<T> {@Overridepublic void method1(T t) {System.out.println(t);}@Overridepublic T method2() {return null;}
}

在创建接口的实现类对象时,确定泛型的类型

public class Test {public static void main(String[] args) {ExampleChild2<String> exampleChild2 = new ExampleChild2<>();exampleChild2.method1("hello");String str = exampleChild2.method2();}
}

三、泛型通配符

泛型通配符<?> ,常用于泛型方法和类中,帮助实现更加灵活和通用的类型操作。

1、<?>

<?>:表示任意类型,适用于我们不关心具体类型的场景。(只能使用Object类中的共性方法)

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

使用举例

public class Test {public static void main(String[] args) {List<Integer> integerList = new ArrayList<>();integerList.add(1);integerList.add(2);// 使用泛型方法printList(integerList);}public static void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}}
}

2、<? extends T>

<? extends T>:表示 TT 的子类型,适用于读取操作。(可以使用父类T 中的公共方法)

public void processNumbers(List<? extends Number> numbers) {for (Number number : numbers) {System.out.println(number.intValue());}Long a = 1L;	 // Long extends Numbernumbers.add(a);  // error,Java 的泛型系统为了类型安全,不允许向这样的列表中添加特定类型的元素(除 null 外)。
}
public <T extends Number> void processNumbers(List<T> numbers) {for (Number number : numbers) {System.out.println(number.intValue());}Long a = 1L;	 // Long extends Numbernumbers.add(a);  // error
}

使用举例

public class Test {public static void main(String[] args) {List<Integer> integerList = new ArrayList<>();integerList.add(1);integerList.add(2);// 使用泛型方法printNumbers(integerList);}public static <T extends Number> void printNumbers(List<T> list) {for (T number : list) {System.out.println(number);}}
}

3、<? super T>

<? super T>:表示 TT 的父类型,适用于写入操作。

public void addNumbers(List<? super Integer> list) {list.add(1); // 可以安全地添加 Integer 类型的元素list.add(2); // 也可以添加其他 Integer 类型的元素
}
// 编译错误! `super` 只能用在 泛型方法 或 类中的方法参数 中来指定一个类型范围。
public <T super Integer> void addNumbers(List<T> list) {...}

使用举例

public class Test {public static void main(String[] args) {List<Number> numbers = new ArrayList<>();// 使用泛型方法addNumbers(numbers);System.out.println(numbers);}public static void addNumbers(List<? super Integer> list) {list.add(1);  // 可以添加 Integer 类型的对象list.add(2);  // 可以添加 Integer 类型的对象}
}

四、泛型的擦除

泛型的擦除是指,在编译期间,Java 编译器会将泛型信息擦除掉,泛型类型参数会被替换为其限定类型。

  • 默认情况下,泛型类型参数会被替换为 Object
  • 如果泛型有上限,如 T extends Number,擦除后会使用上限类型(Number)。

例如:

  • List<String>List<Integer> 在编译后都会被擦除为 List
  • List<T extends Number> 会擦除为 List<Number>

泛型擦除的主要目的是为了 兼容 Java 的早期版本 和 简化虚拟机的实现。

  • 泛型擦除将泛型类型转换为 Object 或其超类,从而使得 泛型代码 可以与 旧版本的 Java 代码 相互操作。
  • 泛型擦除意味着 泛型的具体类型(T)在字节码中不可见,保持向后兼容性的同时,避免对字节码格式进行重大修改。

但是,这也会导致一些泛型相关的信息在运行时不可用,需要在编写泛型代码时注意擦除造成的影响。

1、泛型的擦除

public class GenericMethodExample {// 泛型方法  在擦除后会变成 `print(Object data)` public <T> void print(T data) {System.out.println(data);}// 原始类型的方法 -> 编译错误!因为与擦除后的泛型方法冲突了public void print(Object data) {System.out.println(data);}
}
public class GenericMethodExample {// 泛型方法  在擦除后会变成 `print(Object data)` public <T> void print(T data) {System.out.println(data);}// 原始类型的方法 -> 编译通过!因为泛型默认擦除为 Object,这里是 String,重载public void print(String data) {System.out.println(data);}
}

2、泛型边界的擦除

public void processNumbers(List<? extends Number> numbers) {for (Number number : numbers) {System.out.println(number.intValue());}Long a = 1L;	 // Long extends Numbernumbers.add(a);  // error
}
  • List<T extends Number> 在编译后会被擦除为 List<Number>
public <T extends Number> void processNumbers(List<T> numbers) {for (Number number : numbers) {System.out.println(number.intValue());}Long a = 1L;	 // Long extends Numbernumbers.add(a);  // error
}
  • 泛型参数 T 在运行时被擦除为 Number 类型。

3、无法实例化泛型类型

public class GenericInstantiationExample<T> {private T instance;public GenericInstantiationExample() {// 编译错误:无法直接实例化泛型类型// instance = new T(); // 错误}public GenericInstantiationExample(Class<T> clazz) {try {instance = clazz.getDeclaredConstructor().newInstance();} catch (Exception e) {e.printStackTrace();}}
}
  • new T() 在编译时会引发错误,因为 T 的实际类型在运行时未知。
  • 可以使用反射(通过传递 Class<T> 对象)来实例化对象。

相关文章:

【Java基础】泛型

文章目录 泛型一、概述二、泛型的使用1、类2、方法3、接口 三、泛型通配符1、<?>2、<? extends T>3、<? super T> 四、泛型的擦除1、泛型的擦除2、泛型边界的擦除3、无法实例化泛型类型 泛型 一、概述 泛型&#xff08;Generic&#xff09;是一种机制&a…...

STL-vector练习题

118. 杨辉三角 思路&#xff1a; 杨辉三角有以下性质使我们要用到的&#xff1a; ● 每行数字左右对称&#xff0c;由 1 开始逐渐变大再变小&#xff0c;并最终回到 1。 ● 第 n 行&#xff08;从 0 开始编号&#xff09;的数字有 n1 项&#xff0c;前 n 行共有 2n(n1)个数。…...

Leetcode 165. 比较版本号(Medium)

给你两个 版本号字符串 version1 和 version2 &#xff0c;请你比较它们。版本号由被点 . 分开的修订号组成。修订号的值 是它 转换为整数 并忽略前导零。 比较版本号时&#xff0c;请按 从左到右的顺序 依次比较它们的修订号。如果其中一个版本字符串的修订号较少&#xff0c…...

Android 12 Launcher3 去掉Hotseat

1.概述 在12.0 产品定制化开发中 由产品需求Launcher3 页面布局的原因&#xff0c;要求Launcher3 需要去掉Hotseat 不显示Hotseat下面几个图标&#xff0c;而做满屏app的显示&#xff0c;从而达到美观的效果&#xff0c;下面就来分析去掉Hotseat从而实现这个功能 2.Launcher3 …...

Nginx实用篇:实现负载均衡、限流与动静分离

Nginx实用篇&#xff1a;实现负载均衡、限流与动静分离 | 原创作者/编辑&#xff1a;凯哥Java | 分类&#xff1a;Nginx学习系列教程 Nginx 作为一款高性能的 HTTP 服务器及反向代理解决方案&#xff0c;在互联网架构中扮演着至关重要的角色。它…...

python | Python中的类多态:方法重写和动态绑定

本文来源公众号“python”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Python中的类多态&#xff1a;方法重写和动态绑定 多态&#xff08;Polymorphism&#xff09;是面向对象编程的核心特性之一&#xff0c;它允许同一接口在…...

Rust编写Windows服务

文章目录 Rust编写Windows服务一&#xff1a;Windows服务程序大致原理二&#xff1a;Rust中编写windows服务三&#xff1a;具体实例 Rust编写Windows服务 编写Windows服务可选语言很多, 其中C#最简单。本着练手Rust语言&#xff0c;尝试用Rust编写一个服务。 一&#xff1a;Win…...

MATLAB 从 R2024B 开始支持树莓派 5

树莓派&#xff08;Raspberry Pi&#xff09;系列是一系列基于单板计算机的微型电脑&#xff0c;由英国的树莓派基金会于 2012 年开始发布。它的目标是提供一个低成本、易于学习和玩耍的平台&#xff0c;用于教育和初学者学习计算机科学和编程。 目前市面上&#xff0c;最新最…...

MiniBlogum项目简介

MiniBlogum项目简介 文章目录 MiniBlogum项目简介一、引言二、技术栈与开发环境三、主要功能&#xff08;一&#xff09;用户注册与登录&#xff08;二&#xff09;查看当前登录用户/作者头像、昵称、Gitee仓库地址&#xff08;三&#xff09;查看博客列表&#xff08;四&#…...

如何用 OBProxy 实现 OceanBase 的最佳路由策略

引言 OBProxy&#xff0c;即OceanBase Database Proxy&#xff0c;也简称为ODP&#xff0c;是 OceanBase数据库的专属服务代理。通过应用OBProxy&#xff0c;由后端OceanBase集群的分布式特性所带来的复杂性得以屏蔽&#xff0c;从而使得访问分布式数据库的体验如同访问单机数…...

new/delete和malloc/free到底有什么区别

new和malloc 文章目录 new和malloc前言一、属性上的区别二、使用上的区别三、内存位置的区别四、返回类型的区别五、分配失败的区别六、扩张内存的区别七、系统调度过程的区别总结 前言 new和malloc的知识点&#xff0c;作为一个嵌入式工程师是必须要了解清楚的。new和malloc的…...

Flutter启动无法运行热重载

当出现这种报错时&#xff0c;大概率是flutter的NO_Proxy出问题。 请忽略上面的Android报错因为我做的是windows开发这个也就不管了哈&#xff0c;解决下面也有解决报错的命令大家执行一下就行。 着重说一下Proxy的问题&#xff0c; 我们看到提示NO_PROXY 没有设置。 这个时候我…...

CSS调整背景

一、设置背景颜色 通过 background-color 属性指定&#xff0c;值可以是十六进制 #ffffff&#xff0c;也可以是rgb(0, 255, 255)&#xff0c;或是颜色名称 "red" div {background-color: red; /* 通过颜色名称设置 */background-color: #ff0000; /* 通过十六进制设…...

FinalShell连接Linux服务器并解决反复输入密码问题

FinalShell是一款由国人开发的SSH客户端工具&#xff0c;它支持多平台&#xff0c;包括Windows、Mac OS X和Linux。FinalShell主要用于一体化服务器管理&#xff0c;它不仅是一个SSH客户端&#xff0c;还具备强大的开发和运维功能&#xff0c;能够充分满足开发和运维的需求。 本…...

实用类工具!分享6款AI论文一键生成器免费8000字

在当前的学术研究和写作领域&#xff0c;AI论文生成工具的出现极大地提高了写作效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。千笔-AIPassPaper是一款备受推荐的AI论文一键生成器。 千笔-AIPassPaper是一个一站式…...

vue使用TreeSelect设置带所有父级节点的回显

Element Plus的el-tree-select组件 思路&#xff1a; 选中节点时&#xff0c;给选中的节点赋值 pathLabel&#xff0c;pathLabel 为函数生成的节点名字拼接&#xff0c;数据源中不包含。 在el-tree-select组件中设置 props“{ label: ‘pathLabel’ }” 控制选中时input框中回…...

智能机巢+无人机:自动化巡检技术详解

智能机巢与无人机的结合&#xff0c;在自动化巡检领域展现出了巨大的潜力和优势。以下是对这一技术的详细解析&#xff1a; 一、智能机巢概述 智能机巢&#xff0c;也被称为无人机机场或无人机机巢&#xff0c;是专门为无人机提供停靠、充电、维护等服务的智能化设施。它不仅…...

摩托车加装车载手机充电usb方案/雅马哈USB充电方案开发

长途骑行需要给手机与行车记录仪等设备供电&#xff0c;那么&#xff0c;加装USB充电器就相继在两轮电动车上应用起来了。摩托车加装usb充电方案主要应用于汽车、电动自行车、摩托车、房车、渡轮、游艇等交通工具。提供电动车USB充电器方案/摩托车加装usb充电方案/渡轮加装usb充…...

进阶岛 任务3: LMDeploy 量化部署进阶实践

进阶岛 任务3&#xff1a; LMDeploy 量化部署进阶实践 任务&#xff1a;https://github.com/InternLM/Tutorial/blob/camp3/docs/L2/LMDeploy/task.md 使用结合W4A16量化与kv cache量化的internlm2_5-1_8b-chat模型封装本地API并与大模型进行一次对话&#xff0c;作业截图需包…...

vue 使用jszip,file-saver下载压缩包,自定义文件夹名,文件名打包下载为zip压缩包文件,全局封装公共方法使用。

记录一下后台管理全局封装一个压缩包下载方法&#xff0c;文件夹名自定义&#xff0c;文件名自定义&#xff0c;压缩包名自定义。 安装必要的库 npm install jszip npm install file-saver自定义一个公共方法全局注入 页面使用 /** 下载按钮操作 */handleDownload() {const i…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...