当前位置: 首页 > 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…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...