如何在Java程序中使用泛型

news/2025/3/20 13:12:46/文章来源:https://www.cnblogs.com/icodewalker/p/18782841

如何在Java程序中使用泛型

泛型可以使你的代码更灵活、更易读,并能帮助你在运行时避免ClassCastExceptions。让我们通过这篇结合Java集合框架的泛型入门指南,开启你的泛型之旅。

Java 5引入的泛型增强了代码的类型安全性并提升了可读性。它能帮助你避免诸如ClassCastException(当尝试将对象强制转换为不兼容类型时引发的异常)这类运行时错误。

本教程将解析泛型概念,通过三个结合Java集合框架的实例演示其应用。同时我们将介绍原始类型(raw types),探讨选择使用原始类型而非泛型的场景及其潜在风险。

Java编程中的泛型

  • 为何使用泛型?
  • 如何利用泛型保障类型安全
  • Java集合框架中的泛型应用
  • Java泛型类型示例
  • 原始类型与泛型对比

为何使用泛型?

泛型在Java集合框架中被广泛用于java.util.List、java.util.Set和java.util.Map等接口。它们也存在于Java其他领域,如java.lang.Class、java.lang.Comparable 和java.lang.ThreadLocal。

在泛型出现前,Java代码常缺乏类型安全保障。以下是非泛型时代Java代码的典型示例:

List integerList = new ArrayList();
integerList.add(1);
integerList.add(2);
integerList.add(3);for (Object element : integerList) {Integer num = (Integer) element; // 必须显式类型转换System.out.println(num);
}

这段代码意图存储Integer对象,但没有任何机制阻止你添加其他类型(如字符串):

integerList.add("Hello");

当尝试将String强制转换为Integer时,这段代码会在运行时抛出ClassCastException。

利用泛型保障类型安全

为解决上述问题并避免ClassCastExceptions,我们可以使用泛型指定列表允许存储的对象类型。此时无需手动类型转换,代码更安全且更易理解:

List<Integer> integerList = new ArrayList<>();integerList.add(1);
integerList.add(2);
integerList.add(3);for (Integer num : integerList) {System.out.println(num);
}

List表示"存储Integer对象的列表"。基于此声明,编译器确保只有Integer对象能被添加至列表,既消除了类型转换需求,也预防了类型错误。

Java集合框架中的泛型

泛型深度集成于Java集合框架,提供编译时类型检查并消除显式类型转换需求。当使用带泛型的集合时,你需指定集合可容纳的元素类型。Java编译器基于此规范确保你不会意外插入不兼容对象,从而减少错误并提升代码可读性。

为演示泛型在Java集合框架中的使用,让我们观察几个实例。

List和ArrayList的泛型应用

前例已简要展示ArrayList的基本用法。现在让我们通过List接口的声明深入理解这一概念:

public interface List<E> extends SequencedCollection<E> { … }

此处声明泛型变量为"E",该变量可被任何对象类型替代。注意变量E代表元素(Element)。

接下来演示如何用具体类型替换变量。下例中将替换为

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Challengers");
// list.add(1); // 此行会导致编译时错误

List声明该列表仅能存储String对象。如代码最后一行所示,尝试添加Integer将引发编译错误。

Set和HashSet的泛型应用

Set接口与List类似:

public interface Set<E> extends Collection<E> { … }

我们将用替换,使集合只能存储Double值:

Set<Double> doubles = new HashSet<>();
doubles.add(1.5);
doubles.add(2.5);
// doubles.add("three"); // 编译时错误double sum = 0.0;
for (double d : doubles) {sum += d;
}

Set确保只有Double值能被添加至集合,防止因错误类型转换引发的运行时错误。

Map和HashMap的泛型应用

我们可以声明任意数量的泛型类型。以键值数据结构Map为例,K代表键(Key),V代表值(Value):

public interface Map<K, V> { … }

现在用String替换K作为键类型,用Integer替换V作为值类型:

Map<String, Integer> map = new HashMap<>();
map.put("Duke", 30);
map.put("Juggy", 25);
// map.put(1, 100); // 此行会导致编译时错误

此例展示将String键映射到Integer值的HashMap。添加Integer类型的键将不被允许并导致编译错误。

泛型命名规范

我们可以在任何类中声明泛型类型。虽然可以使用任意名称,但建议遵循命名规范:

  • E 代表元素(Element)
  • K 代表键(Key)
  • V 代表值(Value)
  • T 代表类型(Type)

应避免使用无意义的"X"、"Y"或"Z"等名称。

Java泛型类型使用示例

现在通过更多示例深入演示Java中泛型类型的声明与使用。

创建通用对象容器

我们可以在自定义类中声明泛型类型,不必局限于集合类型。下例中,Box类通过声明泛型类型E来操作任意元素类型。注意泛型类型E声明于类名之后,随后即可作为属性、构造器、方法参数和返回类型使用:

// 定义带泛型参数E的Box类
public class Box<E> {private E content; // 存储E类型对象public Box(E content) { this.content = content; }public E getContent() { return content; }public void setContent(E content) { this.content = content;}public static void main(String[] args) {// 创建存储Integer的BoxBox<Integer> integerBox = new Box<>(123);System.out.println("整数盒内容:" + integerBox.getContent());// 创建存储String的BoxBox<String> stringBox = new Box<>("Hello World");stringBox.setContent("Java Challengers");System.out.println("字符串盒内容:" + stringBox.getContent());}
}

输出结果:

整数盒内容:123
字符串盒内容:Java Challengers

代码要点:

  • Box类使用类型参数E作为容器存储对象的占位符,允许Box处理任意对象类型
  • 构造器初始化Box实例时接受指定类型对象,确保类型安全
  • getContent返回与实例创建时指定的泛型类型匹配的对象,无需类型转换
  • setContent通过类型参数E确保只能设置正确类型的对象
  • main方法创建了存储Integer和String的Box实例
  • 每个Box实例操作特定数据类型,展现泛型在类型安全方面的优势

此例展示了Java泛型的基础实现,演示了如何以类型安全方式创建和操作任意类型对象。

处理多数据类型

我们可以声明多个泛型类型。以下Pair类包含<K, V>泛型值。如需更多泛型参数,可扩展为<K, V, V1, V2, V3>等,代码仍可正常编译。

Pair类示例:

class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() { return key; }public V getValue() { return value; }public void setKey(K key) { this.key = key; }public void setValue(V value) { this.value = value; }
}public class GenericsDemo {public static void main(String[] args) {Pair<String, Integer> person = new Pair<>("Duke", 30);System.out.println("姓名:" + person.getKey());System.out.println("年龄:" + person.getValue());person.setValue(31);System.out.println("更新后年龄:" + person.getValue());}
}

输出结果:

姓名:Duke
年龄:30
更新后年龄:31

代码要点:

  • Pair<K, V>类包含两个类型参数,适用于任意数据类型组合
  • 构造器与方法使用类型参数实现严格类型检查
  • 创建存储String(姓名)和Integer(年龄)的Pair对象
  • 访问器和修改器方法操作Pair数据
  • Pair类可存储管理关联信息而不受特定类型限制,展现泛型的灵活性与强大功能

此例展示泛型如何创建支持多数据类型的可复用类型安全组件,提升代码复用性和可维护性。

让我们再看一个示例。

方法级泛型声明

泛型类型可直接在方法中声明,无需在类级别定义。若某个泛型类型仅用于特定方法,可在方法签名返回类型前声明:

public class GenericMethodDemo {// 声明泛型类型<T>并打印指定类型数组public static <T> void printArray(T[] array) {for (T element : array) {System.out.print(element + " ");}System.out.println();}public static void main(String[] args) {Integer[] intArray = {1, 2, 3, 4};printArray(intArray);String[] stringArray = {"Java", "Challengers"};printArray(stringArray);}
}

输出结果:

1 2 3 4
Java Challengers

原始类型与泛型对比

原始类型指未指定类型参数的泛型类或接口名称。在Java 5引入泛型前,原始类型被广泛使用。现今开发者通常仅在与遗留代码兼容或与非泛型API交互时使用原始类型。即使使用泛型,仍需了解如何识别和处理原始类型。

典型原始类型示例——未指定类型参数的List声明:

 List rawList = new ArrayList();

此处List rawList声明了一个未指定泛型参数的列表。rawList可存储任意类型对象(Integer、String、Double等)。由于未指定类型,编译器不会对添加至列表的对象类型进行检查。

使用原始类型的编译警告

Java编译器会对原始类型使用发出警告,提醒开发者可能存在的类型安全隐患。当使用泛型时,编译器会检查集合(如List、Set)中存储的对象类型、方法返回类型和参数是否匹配声明类型,从而预防如ClassCastException的常见错误。

使用原始类型时,由于未指定存储对象类型,编译器无法进行类型检查,因此会发出警告提示你绕过了泛型提供的类型安全机制。

编译警告示例

以下代码演示编译器如何对原始类型发出警告:

List list = new ArrayList(); // 警告:原始使用参数化类'List'
list.add("hello");
list.add(1);

编译时通常会显示:

注意:SomeFile.java使用了未经检查或不安全的操作。
注意:使用-Xlint:unchecked重新编译以获取详细信息。

使用-Xlint:unchecked参数编译将显示更详细警告:

warning: [unchecked] unchecked call to add(E) as a member of the raw type Listlist.add("hello");^where E is a type-variable:E extends Object declared in interface List

若确信使用原始类型不会引入风险,或处理无法重构的遗留代码,可使用@SuppressWarnings("unchecked")注解抑制警告。但需谨慎使用,避免掩盖真实问题。

使用原始类型的后果

尽管原始类型有助于向后兼容,但存在两大缺陷:类型安全性缺失和维护成本增加。

  • 类型安全性缺失:泛型的核心优势是类型安全,使用原始类型将丧失这一优势。编译器不进行类型正确性检查,可能导致运行时ClassCastException。
  • 维护成本增加:使用原始类型的代码缺乏泛型提供的明确类型信息,维护难度加大,易产生仅在运行时暴露的错误。

类型安全问题示例:使用原始类型List而非泛型List时,编译器允许添加任意类型对象。当从列表检索元素并尝试强制转换为String时,若实际为其他类型将导致运行时错误。

泛型知识要点回顾

泛型以高度灵活性提供类型安全保障。以下回顾关键要点:

泛型是什么?为何使用?

  • code.Java 5引入泛型以提升代码类型安全性和灵活性
  • 主要优势在于帮助避免ClassCastException等运行时错误
  • 泛型广泛应用于Java集合框架,也见于Class、Comparable、ThreadLocal等组件
  • 通过阻止不兼容类型插入实现类型安全

Java集合中的泛型

  • List和ArrayList:List允许指定元素类型E,确保列表类型专一
  • Set和HashSet:Set限定元素为类型E,保持一致性
  • Map和HashMap:Map<K,V>定义键值类型,提升类型安全性和代码清晰度

泛型使用优势

  • 通过阻止不兼容类型插入减少错误
  • 明确类型关联提升代码可读性和可维护性
  • 便于以类型安全方式创建和管理集合等数据结构

  • 【注】本文译自: How to use generics in your Java programs | InfoWorld

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

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

相关文章

BUUCTF从0到1:N1BOOk(web入门,常见的信息收集)

各位师傅们,第一次写博客文章: 就从最简单的web信息收集入门吧: 信息收集在不管时在红蓝对抗还是在ctf比赛中都是具有很大的作用 我们先看题:题目提示:信息收集,我们就直接打开虚拟机kali用dirsearch命令扫描网址: 对于新手来说dirsearch还未安装,dirsearch安装看这个:…

小白也能搞定系统搭建!7步教你选对开发平台!

你有没有遇到过这种情况: —— 想搞个客户管理系统,结果一搜教程,满屏都是代码,瞬间头大? —— 公司事儿一堆,老板还不想花钱买现成的系统,非要我们自己搭,可咱也不会写代码啊,这可咋整? 其实,现在做系统根本不用写代码,有些工具拖拽一下就能搭建,小白也能轻松上手…

如何学习 ROS+PX4

博客地址:https://www.cnblogs.com/zylyehuo/参考 https://www.bilibili.com/video/BV1vx4y1Y7Tu?spm_id_from=333.788.player.switch&vd_source=4acdb875c05ce9dccfce3cd6cfaac651

Mavros Mavlink

博客地址:https://www.cnblogs.com/zylyehuo/参考 https://www.bilibili.com/video/BV1x841167uG?spm_id_from=333.788.videopod.sections&vd_source=4acdb875c05ce9dccfce3cd6cfaac651

用于太阳能电池板的线性电机物联网控制器

随着能源成本的增加,太阳能电池板显然是减少这些费用的一条途径。即使有一对面板在一串(两个串联),和一个电网限制电流逆变器(如优秀的SUN-1000GTIL2和SUN-2000GTIL2系列),你可以节省很多钱-即使在冬天!后面的文章将详细介绍各种具有成本效益的选项和经验。 LMIC LMIC(线性电…

leetcode每日一题:最少翻转操作数

题目 2612. 最少翻转操作数 给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p ,它们表示一个长度为 n 且下标从 0 开始的数组 arr ,数组中除了下标为 p 处是 1 以外,其他所有数都是 0 。 同时给你一个整数数组 banned ,它包含数组中的一些位置。banned 中第 i 个位置表…

黄色网站破解

最近再查资料, 莫名其妙弹窗黄色网站下载页面, 我的手机是iOS非越狱版本, 我当然是敢点击的, 即使有病毒也不怕。 51duhui是虚假的应用, 假冒app store风格下载安装, 狗日的,发现是下载mobileconfig, 看下下载的按钮,代码如下:function jumpurl(url) {setTimeout(f…

“人工智能+”智赋千行百业!

今年,DeepSeek在AI赛道一骑绝尘 以“火炎焱燚”之势迅速延伸 开启中国AI黄金时代 如今,中国的AI故事正在书写新篇 中国的科技强国之路也正越走越宽在DeepSeek引领的人工智能热潮中 作为云服务国家队 天翼云“息壤”智算平台率先完成 国产算力与DeepSeek-R1/V3 系列大模型的深…

使用Kettle将sqlserver库表结构和数据导入到oracle

一、官网下载kettle的压缩包pdi-ce-9.4.0.0-343.zip ,下载jtds-1.3.1.jar和ojdbc8-19.3.0.0.jar 将jar放入\data-integration\lib 目录下二、将zip解压后,是一个data-integration文件夹,找到spoon.bat 双击运行,即可打开kettle工具三、打开以后,右键点击转换,新建一个转换…

Windows 11 24H2 中文版、英文版 (x64、ARM64) 下载 (2025 年 3 月更新)

Windows 11 24H2 中文版、英文版 (x64、ARM64) 下载 (2025 年 3 月更新)Windows 11 24H2 中文版、英文版 (x64、ARM64) 下载 (2025 年 3 月更新) Windows 11, version 24H2 Enterprise Arm64 x64 (updated Mar 2025) 请访问原文链接:https://sysin.org/blog/windows-11/ 查看最…

作业三

问题 内容这个作业属于哪个课程 课程链接这个作业要求在哪里 作业要求这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序姓名 学号何松 3123004786洪徐博 3123004747PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)Planning 计划 Esti…

VMware Live Site Recovery 9.0.2.2 发布 - 数据中心灾难恢复 (DR)

VMware Live Site Recovery 9.0.2.2 发布 - 数据中心灾难恢复 (DR)VMware Live Site Recovery 9.0.2.2 发布 - 数据中心灾难恢复 (DR) Site Recovery Manager 9.0 Update 2 请访问原文链接:https://sysin.org/blog/vmware-live-site-recovery-9/ 查看最新版。原创作品,转载请…