[Java]泛型(一)泛型类

 1. 什么是泛型类?

泛型类是指类中使用了占位符类型(类型参数)的类。通过使用泛型类,你可以编写可以处理多种数据类型的代码,而无需为每种类型编写单独的类。泛型类使得代码更具通用性和可重用性,同时可以保证类型安全。

2. 泛型类的基本语法

2.1 泛型类的定义通常包含:

  • 类型参数:通过尖括号< >指定,占位符通常是字母,比如TEKV等。
  • 类定义:类的成员变量、方法等可以使用类型参数。

2.2 泛型类的语法格式:

class ClassName<T> {  // T 是泛型类型参数// 类的成员变量private T value;// 泛型类的构造方法public ClassName(T value) {this.value = value;}// 泛型类的方法public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}

3. 如何使用泛型类

在你定义好泛型类之后,你可以在实例化时指定具体的类型,告诉泛型类使用什么类型。

3.1 示例:使用泛型类

class Box<T> {private T value;  // 存储泛型类型的数据// 构造方法public Box(T value) {this.value = value;}// 获取值的方法public T getValue() {return value;}// 设置值的方法public void setValue(T value) {this.value = value;}
}public class generic_type {public static void main(String[] args) {// 创建一个泛型类的对象,指定类型为 StringBox<String> stringBox = new Box<>("Hello");System.out.println(stringBox.getValue());  // 输出 "Hello"// 创建一个泛型类的对象,指定类型为 IntegerBox<Integer> intBox = new Box<>(123);System.out.println(intBox.getValue());  // 输出 123// 创建一个泛型类的对象,指定类型为 DoubleBox<Double> doubleBox = new Box<>(45.67);System.out.println(doubleBox.getValue());  // 输出 45.67// 修改值stringBox.setValue("Goodbye");System.out.println(stringBox.getValue());  // 输出 "Goodbye"}
}

3.2 代码解释:

  1. Box<T>:这是一个泛型类,类名是 BoxT 是类型参数。T 可以代表任何数据类型,具体的类型将在创建对象时指定。

  2. private T valueBox 类中定义了一个成员变量 value,它的数据类型是泛型 T,这意味着它可以存储任何类型的数据。

  3. 构造方法 public Box(T value):构造方法接收一个类型为 T 的参数,表示初始化时将传入的数据赋值给 value

  4. getValue()setValue(T value):这两个方法分别用于获取和设置 value 的值。它们的类型都是 T,所以可以支持任意类型的数据。

3.3 在 Main 类中的应用:

  • Box<String> stringBox = new Box<>("Hello");:创建一个 Box 类型的对象,指定 TString 类型,初始化时给 value 赋值为 "Hello"
  • System.out.println(stringBox.getValue());:调用 getValue() 方法输出 "Hello"
  • Box<Integer> intBox = new Box<>(123);:创建一个 Box 类型的对象,指定 TInteger 类型,初始化时给 value 赋值为 123
  • Box<Double> doubleBox = new Box<>(45.67);:创建一个 Box 类型的对象,指定 TDouble 类型,初始化时给 value 赋值为 45.67

4.扩展:将Box作为内部类和外部类的区别

4.1 将Box作为外部类(如上示例的代码就是将Box作为外部类使用的)

4.2 将Box作为内部类使用

错误示例:

public class generic_type {class Box<T>{private T value;public Box(T value){this.value = value;}public T getValue(){return value;}public void setValue(T value){this.value = value;}}public static void main(String[] args){Box<String> stringBox = new Box<>("Hello");//报错System.out.println(stringBox.getValue());Box<Integer> intBox = new Box<>(123);//报错System.out.println(intBox.getValue());Box<Double> doubleBox = new Box<>(45.67);//报错System.out.println(doubleBox.getValue());stringBox.setValue("Goodbye!");System.out.println(stringBox.getValue());}}

为什么会出现这样的错误: 如果 Box 类定义在 generic_type 类内部,它是一个 成员内部类Box 类只有在 generic_type 类的实例化对象中才有意义,且在外部无法直接使用 Box 类,必须通过 generic_type 的实例来访问它。

修改过后正确的内部类:

public class GenericType {class Box<T> {private T value;public Box(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}}public static void main(String[] args) {// 必须先创建外部类 GenericType 的对象GenericType genericType = new GenericType();GenericType.Box<String> stringBox = genericType.new Box<>("Hello");System.out.println(stringBox.getValue());}
}

4.3 使用内部类和外部类的区别:

  1. 外部类定义 Box<T>
    1. 独立性更强Box 类是独立的,可以单独作为一个类使用。比如,如果你在同一个包中的其他地方需要使用 Box 类,就不需要创建 generic_type 类的实例,可以直接创建 Box 的实例。
    2. 访问范围更广Box 类的作用域是整个包内或者是类所在的文件中,其他地方可以直接使用它。
  2. 内部类定义 Box<T>
    • 依赖于外部类:如果 Boxgeneric_type 类的成员(内部类),那么必须先创建 generic_type 类的对象,然后通过这个对象来创建 Box 的实例。
    • 访问范围较小Box 只能在 generic_type 类内部或通过 generic_type 的对象访问,限制了它的使用范围。其他类无法直接访问它,除非通过 generic_type 类实例。

5.泛型类的优点

  • 代码复用:通过泛型类,你不需要为每种数据类型编写不同的类,节省了代码量。
  • 类型安全:编译器可以在编译时检查类型是否匹配,避免了运行时错误和类型转换异常。
  • 灵活性:同一个类可以处理不同类型的数据,例如Box<String>Box<Integer>

6.泛型类的应用场景

6.1 应用场景

  • 集合类:Java的集合框架(如ListSetMap)广泛使用泛型类。你可以创建类型安全的集合。
  • 容器类:泛型类常用于实现容器类,比如将某个类型的元素封装在类中。
  • 工具类:很多通用的工具类,例如Java中的Collections类,也使用泛型来提高灵活性和类型安全。

6.2 示例:

6.2.1 使用泛型类实现一个集合类

// 泛型容器类 Container
public class Container<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}public static void main(String[] args) {// 使用 Container 存储 Integer 类型数据Container<Integer> intContainer = new Container<>();intContainer.setValue(42);System.out.println(intContainer.getValue());  // 输出 42// 使用 Container 存储 String 类型数据Container<String> strContainer = new Container<>();strContainer.setValue("Hello");System.out.println(strContainer.getValue());  // 输出 Hello}
}

6.2.2 使用泛型类实现一个容器类

// 泛型类:容器类
class Container<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}
}public class Main {public static void main(String[] args) {// 创建容器实例,用String类型Container<String> stringContainer = new Container<>();stringContainer.setValue("Hello");System.out.println(stringContainer.getValue());  // 输出 Hello// 创建容器实例,用Integer类型Container<Integer> intContainer = new Container<>();intContainer.setValue(100);System.out.println(intContainer.getValue());  // 输出 100}
}

6.2.3 使用泛型实现一个工具类 

// 泛型工具类 GenericUtils
public class GenericUtils {// 泛型方法:交换数组中的两个元素public static <T> void swap(T[] array, int i, int j) {T temp = array[i];array[i] = array[j];array[j] = temp;}// 泛型方法:打印数组元素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 数组Integer[] intArray = {1, 2, 3, 4};swap(intArray, 0, 2);printArray(intArray);  // 输出:3 2 1 4// 使用泛型工具类处理 String 数组String[] strArray = {"apple", "banana", "cherry"};swap(strArray, 0, 1);printArray(strArray);  // 输出:banana apple cherry}
}

7. 泛型类的多个类型参数

7.1 解释

一个泛型类可以使用多个类型参数。例如,你可以定义一个包含键值对的容器类(如Map),它就需要两个类型参数,一个表示,一个表示

7.2 示例:使用多个类型参数

// 定义一个包含键值对的泛型类
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 class Main {public static void main(String[] args) {// 创建一个泛型Pair对象,指定键为String,值为IntegerPair<String, Integer> pair = new Pair<>("age", 25);System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());}
}

8.泛型类的继承

8.1 解释

泛型类的继承 是指在 Java 中通过继承泛型类来创建新的类。继承泛型类时,可以选择固定泛型类型或保留泛型类型占位符。具体的做法取决于是否希望在子类中指定一个具体的类型,或者希望子类保持泛型类型。

8.2 泛型类继承类型

8.2.1 子类继承泛型类并保留泛型类型

如果我们希望在子类中保持泛型类型(即让子类继续使用泛型),可以通过在子类中指定泛型类型。

示例:子类继承泛型类并保留泛型类型
// 泛型父类
class Box<T> {private T value;public Box(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}// 泛型子类,保留父类的泛型类型
class ColoredBox<T> extends Box<T> {private String color;public ColoredBox(T value, String color) {super(value);this.color = color;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}
}public class Main {public static void main(String[] args) {// 使用 Integer 类型ColoredBox<Integer> intBox = new ColoredBox<>(123, "Red");System.out.println("Value: " + intBox.getValue() + ", Color: " + intBox.getColor());// 使用 String 类型ColoredBox<String> strBox = new ColoredBox<>("Hello", "Blue");System.out.println("Value: " + strBox.getValue() + ", Color: " + strBox.getColor());}
}
/*
输出:
Value: 123, Color: Red
Value: Hello, Color: Blue
*/
解释:
  • Box<T> 是一个泛型父类,表示一个可以存储任何类型 T 的盒子。
  • ColoredBox<T> 继承自 Box<T>,它保留了泛型 T,并增加了一个 color 属性。
  • main 方法中,我们通过创建 ColoredBox<Integer>ColoredBox<String> 来使用这个泛型子类,分别存储整数和字符串。

8.2.2 子类继承泛型类并指定具体的类型

有时你希望子类继承泛型类并指定一个具体的类型。这时,可以在继承时直接指定泛型类型,而不保留泛型类型参数。你可以在子类中明确指定泛型类型(通常是常见的基本类型或子类型)。

示例:子类继承泛型类并指定具体类型
// 泛型父类
class Box<T> {private T value;public Box(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}// 子类继承泛型父类,并指定具体的类型
class IntegerBox extends Box<Integer> {public IntegerBox(Integer value) {super(value);}public void printSquaredValue() {Integer value = getValue();System.out.println("Squared value: " + (value * value));}
}public class Main {public static void main(String[] args) {// 使用 Integer 类型IntegerBox integerBox = new IntegerBox(5);System.out.println("Value: " + integerBox.getValue());integerBox.printSquaredValue();  // 输出:Squared value: 25}
}
/*
输出:
Value: 5
Squared value: 25
*/
解释:
  • Box<T> 是一个泛型父类,表示一个可以存储任何类型 T 的盒子。
  • IntegerBox 是一个子类,它继承了 Box<Integer>,并指定了 TInteger 类型。
  • IntegerBox 具有一个 printSquaredValue 方法,演示如何使用从父类继承的 getValue 方法。

8.2.3 子类继承泛型类并限制泛型的类型边界

你可以在子类中为泛型类型指定 边界(bounds),即限制泛型类型必须是某个特定类或接口的子类。例如,你可以让泛型类型 T 限定为 Number 或其子类。

示例:使用类型边界的泛型继承
// 泛型父类,限定 T 必须是 Number 的子类
class NumericBox<T extends Number> {private T value;public NumericBox(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}public void printSquare() {System.out.println("Squared value: " + (value.doubleValue() * value.doubleValue()));}
}// 子类继承泛型父类,并指定具体类型
class IntegerBox extends NumericBox<Integer> {public IntegerBox(Integer value) {super(value);}
}class DoubleBox extends NumericBox<Double> {public DoubleBox(Double value) {super(value);}
}public class Main {public static void main(String[] args) {// 使用 Integer 类型IntegerBox integerBox = new IntegerBox(5);integerBox.printSquare();  // 输出:Squared value: 25.0// 使用 Double 类型DoubleBox doubleBox = new DoubleBox(3.14);doubleBox.printSquare();  // 输出:Squared value: 9.8596}
}
/*
输出:
Squared value: 25.0
Squared value: 9.8596
*/
解释:
  • NumericBox<T extends Number> 是一个带有类型边界的泛型类,限定了 T 必须是 Number 或其子类。
  • IntegerBoxDoubleBox 分别继承 NumericBox<Integer>NumericBox<Double>,实现了不同类型的继承。
  • printSquare 方法中,调用了 value.doubleValue(),这在 Number 类中是定义好的,因此可以安全地对数值进行平方运算。

8.3 注意事项

  • 泛型类型的擦除: 泛型类型在编译时会进行类型擦除(type erasure),所以在运行时泛型类型会被替换为它的边界类型(例如 ObjectNumber)。这就是为什么泛型类不能直接创建数组。
  • 泛型和继承: 泛型类和子类之间的继承关系可以保留泛型类型,也可以指定具体的类型。你可以通过继承来扩展泛型类的功能,但要注意类型边界的使用。
  • 类型安全: 泛型的使用保证了类型安全,通过编译时的类型检查,避免了运行时类型错误,减少了类型强制转换的需要。

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

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

相关文章

模型I/O功能之模型包装器

文章目录 模型包装器分类LLM模型包装器、聊天模型包装器 截至2023年7月&#xff0c;LangChain支持的大语言模型已经超过了50种&#xff0c;这其中包括了来自OpenAI、Meta、Google等顶尖科技公司的大语言模型&#xff0c;以及各类优秀的开源大语言模型。对于这些大语言模型&…

【漫话机器学习系列】067.希腊字母(greek letters)-写法、名称、读法和常见用途

希腊字母&#xff08;Greek Letters&#xff09; 希腊字母在数学、科学、工程学和编程中广泛使用&#xff0c;常用于表示变量、常量、参数、角度等。以下是希腊字母的完整列表及其常见用途。 大写与小写希腊字母表 大写小写名称&#xff08;英文&#xff09;名称&#xff08;…

JxBrowser 7.41.7 版本发布啦!

JxBrowser 7.41.7 版本发布啦&#xff01; • 已更新 #Chromium 至更新版本 • 实施了多项质量改进 &#x1f517; 点击此处了解更多详情。 &#x1f193; 获取 30 天免费试用。

AI在自动化测试中的伦理挑战

在软件测试领域&#xff0c;人工智能&#xff08;AI&#xff09;已经不再是遥不可及的未来技术&#xff0c;而是正在深刻影响着测试过程的现实力量。尤其是在自动化测试领域&#xff0c;AI通过加速测试脚本生成、自动化缺陷检测、测试数据生成等功能&#xff0c;极大提升了测试…

单片机基础模块学习——超声波传感器

一、超声波原理 左边发射超声波信号&#xff0c;右边接收超声波信号 左边的芯片用来处理超声波发射信号&#xff0c;中间的芯片用来处理接收的超声波信号 二、超声波原理图 T——transmit 发送R——Recieve 接收 U18芯片对输入的N_A1信号进行放大&#xff0c;然后输入给超声…

蓝桥杯之c++入门(一)【C++入门】

目录 前言5. 算术操作符5.1 算术操作符5.2 浮点数的除法5.3 负数取模5.4 数值溢出5.5 练习练习1&#xff1a;计算 ( a b ) ⋆ c (ab)^{\star}c (ab)⋆c练习2&#xff1a;带余除法练习3&#xff1a;整数个位练习4&#xff1a;整数十位练习5&#xff1a;时间转换练习6&#xff…

Redis --- 分布式锁的使用

我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法&#xff0c;但是这样不能让锁跨JVM也就是跨进程去使用&#xff0c;只能适用在单体项目中如下图&#xff1a; 为了解决这种场景&#xff0c;我们就需要用一个锁监视器对全部集群进行监视…

房屋租赁系统在数字化时代中如何重塑租赁服务与提升市场竞争力

内容概要 在当今快速发展的数字化时代&#xff0c;房屋租赁系统的作用愈发重要。随着市场需求的变化&#xff0c;租赁服务正面临着新的挑战与机遇。房屋租赁系统不仅仅是一个简单的管理工具&#xff0c;更是一个能够提升用户体验和市场竞争力的重要平台。其核心功能包括合同管…

INCOSE需求编写指南-附录 D: 交叉引用矩阵

附录 Appendix D: 交叉引用矩阵 Cross Reference Matrices Rules to Characteristics Cross Reference Matrix NRM Concepts and Activities to Characteristics Cross Reference Matrix Part 1 NRM Concepts and Activities to Characteristics Cross Reference Matrix Part…

Java---入门基础篇(上)

前言 本片文章主要讲了刚学Java的一些基础内容,例如注释,标识符,数据类型和变量,运算符,还有逻辑控制等,记录的很详细,带你从简单的知识点再到练习题.如果学习了c语言的小伙伴会发现,这篇文章的内容和c语言大致相同. 而在下一篇文章里,我会讲解方法和数组的使用,也是Java中基础…

新版231普通阿里滑块 自动化和逆向实现 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向过程 补环境逆向 部分补环境 …

探索AI(chatgpt、文心一言、kimi等)提示词的奥秘

大家好&#xff0c;我是老六哥&#xff0c;我正在共享使用AI提高工作效率的技巧。欢迎关注我&#xff0c;共同提高使用AI的技能&#xff0c;让AI成功你的个人助理。 "AI提示词究竟是什么&#xff1f;" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…

商密测评题库详解:商用密码应用安全性评估从业人员考核题库详细解析(9)

1. 申请商用密码测评机构需提交材料考点 根据《商用密码应用安全性测评机构管理办法(试行)》,申请成为商用密码应用安全性测评机构的单位应当提交的材料不包括( )。 A. 从事与普通密码相关工作情况的说明 B. 开展测评工作所需的软硬件及其他服务保障设施配备情况 C. 管…

Flink中的时间和窗口

在批处理统计中&#xff0c;我们可以等待一批数据都到齐后&#xff0c;统一处理。但是在实时处理统计中&#xff0c;我们是来一条就得处理一条&#xff0c;那么我们怎么统计最近一段时间内的数据呢&#xff1f;引入“窗口”。 所谓的“窗口”&#xff0c;一般就是划定的一段时…

Linux 进程概念

目录 一、前言 二、概念实例&#xff0c;正在执行的程序等 三、描述进程-PCB 四、组织进程 五、查看进程 ​编辑六、通过系统调用获取进程标示符 七、进程切换和上下文数据 1.进程切换 2.上下文数据 一、前言 在Linux中&#xff0c;每个执行的程序叫做进程&#xff…

allegro修改封闭图形线宽

说在前面 我们先把最优解说在前面,然后后面再说如果当时不熟悉软件的时候为了挖孔是用了shapes该怎么修改回来。 挖空最方便的方式是在cutout层画一个圆弧,下面开始图解,先add一个圆弧 z 最好是在画的时候就选择好层,如果忘记了后续再换回去也行,但好像软件有bug,此处并…

使用openwrt搭建ipsec隧道

背景&#xff1a;最近同事遇到了个ipsec问题&#xff0c;做的ipsec特性&#xff0c;ftp下载ipv6性能只有100kb, 正面定位该问题也蛮久了&#xff0c;项目没有用openwrt, 不过用了开源组件strongswan, 加密算法这些也是内核自带的&#xff0c;想着开源的不太可能有问题&#xff…

Day29(补)-【AI思考】-精准突围策略——从“时间贫困“到“效率自由“的逆袭方案

文章目录 精准突围策略——从"时间贫困"到"效率自由"的逆袭方案**第一步&#xff1a;目标熵减工程&#xff08;建立四维坐标&#xff09;** 与其他学习方法的结合**第二步&#xff1a;清华方法本土化移植** 与其他工具对比**~~第三步&#xff1a;游戏化改造…

手撕Diffusion系列 - 第十一期 - lora微调 - 基于Stable Diffusion(代码)

手撕Diffusion系列 - 第十一期 - lora微调 - 基于Stable Diffusion&#xff08;代码&#xff09; 目录 手撕Diffusion系列 - 第十一期 - lora微调 - 基于Stable Diffusion&#xff08;代码&#xff09;Stable Diffusion 原理图Stable Diffusion的原理解释Stable Diffusion 和Di…

vscode+WSL2(ubuntu22.04)+pytorch+conda+cuda+cudnn安装系列

最近在家过年闲的没事&#xff0c;于是研究起深度学习开发工具链的配置和安装&#xff0c;之前欲与天公试比高&#xff0c;尝试在win上用vscodecuda11.6vs2019的cl编译器搭建cuda c编程环境&#xff0c;最后惨败&#xff0c;沦为笑柄&#xff0c;痛定思痛&#xff0c;这次直接和…