Java之泛型

Java之泛型

目录

泛型类

语法

使用

泛型上界

定义

使用

类型擦除

通配符

通配符上界

 通配符下界

泛型方法

语法

使用 

泛型的限制


泛型类
语法
class 泛型类名称<类型形参列表> {
        // 这里可以使用类型参数
}


class ClassName<T1, T2, ..., Tn> {
}



class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
        // 这里可以使用类型参数
}


class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
        // 可以只使用部分类型参数
}
使用
泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象 
class MyArray<T> { public Object[] array = new Object[10]; public void set(int pos,T val) { array[pos] = val; } public T get(int pos) { return (T)array[pos]; } public Object[] getArray() { return array; } } public class Test { public static void main(String[] args) { MyArray<String> myArray = new MyArray<>(); } }
泛型上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。

定义
class 泛型类名称<类型形参 extends 类型边界> {
        ...
使用
class MyArray<T extends Number> { public Object[] array = new Object[10]; public void set(int pos,T val) { array[pos] = val; } public T get(int pos) { return (T)array[pos]; } public Object[] getArray() { return array; } } public class Test { public static void main(String[] args) { // 编译错误,因为 String 不是 Number 的子类型 // MyArray<String> myArray1 = new MyArray<>(); // 正常,因为 Integer 是 Number 的子类型 MyArray<Integer> myArray = new MyArray<>(); } }
class Alg<T extends Comparable<T>> { public T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(array[i].compareTo(max) > 0) { max = array[i]; } } return max; } }

类型擦除

Java中的泛型擦除是Java泛型实现的一个关键特性,它允许Java在编译时检查类型安全,但在运行时忽略泛型信息。这意味着,尽管你可以在编译时指定泛型类型,但在运行时,JVM看到的只是原始类型,并不保留任何关于泛型参数的信息。

class MyArray<E> { // E 会被擦除为 Object } class MyArray<E extends Comparable<E>> { // E 被擦除为 Comprable }

 示例

class Alg<T extends Comparable<T>> { public T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(array[i].compareTo(max) > 0) { max = array[i]; } } return max; } }

针对上面的代码,编译结果如下:

编译器在类型擦除阶段做什么?

一、擦除泛型类型信息
移除类型参数:编译器会移除所有与泛型相关的类型参数信息。这意味着在编译后的字节码中,将不再包含任何泛型类型参数的信息。
替换为原始类型:泛型类型参数会被替换为其上界(如果指定了上界的话,比如T extends Comparable<T>中的T会被替换为Comparable<T>;如果没有指定上界,则默认为Object)。例如,List<String>和List<Integer>在编译后的字节码中都表现为List。
二、调整类型信息
方法参数、局部变量和字段的类型调整:除了泛型类型本身被擦除外,编译器还会相应地调整方法参数、局部变量和字段的类型,以确保它们在运行时的一致性。
三、插入类型转换
类型强制转换:由于类型信息在运行时不可用,编译器会在需要的地方插入必要的类型转换。例如,从List<String>中获取元素时,虽然运行时这个列表只是普通的List,但编译器会在幕后添加一个从Object到String的显式类型转换。
四、确保类型安全
编译时类型检查:在编译阶段,编译器会利用泛型类型信息进行类型检查,确保类型安全。例如,编译器会阻止向List<String>中添加整数或其他非字符串对象,或者尝试将List<Integer>赋值给List<Double>等不兼容的操作。
五、桥接方法的生成
向后兼容性:为了保持与未使用泛型的旧代码的兼容性,编译器可能会为泛型类生成桥接方法。这些桥接方法允许泛型类的方法与原生类型的方法共存,并且可以相互调用。
总的来说,编译器在类型擦除阶段的主要目标是确保泛型代码在运行时能够安全、正确地执行,同时尽量减少对现有代码的影响。通过移除泛型类型信息并插入必要的类型转换,编译器使得泛型代码能够与非泛型代码无缝对接,从而保证了Java平台的整体一致性和稳定性。 
通配符
?用于在泛型的使用,即为通配符。
class Message<T> { private T message; public T getMessage() { return message; } public void setMessage(T message) { this.message = message; } } public class Test{ //可以传入任意类型的Message public static void fun(Message<?> temp){ System.out.println(temp.getMessage()); } public static void main(String[] args) { Message<String> message = new Message<>(); message.setMessage("你好!"); fun(message); Message<Integer> message2 = new Message<>(); message2.setMessage(199); fun(message2); } }
通配符上界
<? extends 上界> 
上界通配符用于指定一个类型的上界,即泛型参数必须是该类型或其子类。这种通配符主要用于读取操作,因为它允许你安全地读取数据,但不允许你写入数据(除了null,因为null可以被视为任何类型的实例)。

特点:

add()方法:不能添加任何元素(除了null),因为编译器无法确定具体的类型,添加其他元素可能会导致类型安全问题。
get()方法:可以安全地读取元素,并且返回的类型是T或T的子类,但通常需要用T或T的父类(如Object)来接收,因为编译器无法确定具体的子类类型。
class Food { } class Fruit extends Food { } class Apple extends Fruit { } class Banana extends Fruit { } class Message<T> { private T message; public T getMessage() { return message; } public void setMessage(T message) { this.message = message; } } public class Test1 { public static void funExtends(Message<? extends Fruit> temp){ System.out.println(temp.getMessage()); //通配符的上界来说 是不可以进行修改元素的 /* temp.setMessage(new Banana()); temp.setMessage(new Apple());*/ //向上转型 Fruit fruit = temp.getMessage(); } public static void main(String[] args) { Message<Apple> message1 = new Message<>(); message1.setMessage(new Apple()); Message<Banana> message2 = new Message<>(); message2.setMessage(new Banana()); funExtends(message1); funExtends(message2); } }
 通配符下界
<? super 下界> 
下界通配符用于指定一个类型的下界,即泛型参数必须是该类型或其父类。这种通配符主要用于写入操作,因为它允许你安全地添加T或T的子类类型的元素,但在读取时只能确保是Object类型。 

特点:

add()方法:可以添加T或T的子类类型的元素,因为所有这些类型都是下界类型的子类或相同类型。
get()方法:读取元素时,由于类型的不确定性,通常只能用Object类型来接收。如果需要具体的类型,则需要进行类型转换。 
class Food { } class Fruit extends Food { } class Apple extends Fruit { } class Banana extends Fruit { } class Plate<T> { private T plate ; public T getPlate() { return plate; } public void setPlate(T plate) { this.plate = plate; } } public class Test1 { public static void funSuper(Plate<? super Fruit> temp){ // 此时可以修改!!添加的是Fruit 或者Fruit的子类 temp.setPlate(new Apple());//这个是Fruit的子类 向上转型 temp.setPlate(new Banana());//这个是Fruit的本身 // Fruit fruit = temp.getPlate(); //不能接收,这里无法确定是哪个父类 System.out.println(temp.getPlate());//只能直接输出 } public static void main(String[] args) { Plate<Fruit> message1 = new Plate<>(); Plate<Food> message2 = new Plate<>(); funSuper(message1); funSuper(message2); } }
泛型方法
语法
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
使用 
class Alg2 { //泛型方法 public static <T extends Comparable<T>> T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(array[i].compareTo(max) > 0) { max = array[i]; } } return max; } } public class Test2 { public static void main1(String[] args) { Integer[] array = {1,13,51,71,19}; Integer ret = Alg2.<Integer>findMax(array); System.out.println(ret); } }
泛型的限制
1. 不能使用基本数据类型作为泛型类型参数
Java中的泛型只能使用引用数据类型作为类型参数,不能使用基本数据类型(如int、double等)。如果需要使用基本数据类型的泛型,通常需要使用它们对应的包装类(如Integer、Double等)。

2. 不能创建泛型类型的数组
由于类型擦除的原因,Java不允许直接创建泛型类型的数组。例如,List<String>[] listArray = new ArrayList<String>[10]; 是不允许的。如果需要类似的功能,可以考虑使用ArrayList<List<String>>等集合类来实现。

3. 不能实例化泛型类型的对象(除了使用new操作符的匿名内部类)
Java不允许直接实例化泛型类型的对象,如List<String> list = new List<String>(); 是不允许的。应该使用具体的类来实例化泛型,如List<String> list = new ArrayList<>();。

4. 运行时类型查询的限制
由于类型擦除,在运行时无法直接查询泛型参数的具体类型。例如,instanceof 运算符和getClass()方法不能用于检查泛型类型参数的具体类型。这意味着,在运行时,所有的泛型类型都会被当作它们的原始类型(即擦除后的类型)来处理。

5. 泛型类型参数不具有继承关系
即使两个类之间存在继承关系,它们的泛型类型参数之间也不具有继承关系。例如,List<A>并不是List<B>的子类,即使A是B的子类。这要求开发者在编写泛型代码时特别注意类型安全。

6. 泛型上界和下界的限制
上界:通过extends关键字,可以限制泛型类型参数必须是某个类型的子类或实现类。这有助于在编译时检查类型兼容性,提高代码的安全性。
下界:通过super关键字,可以限制泛型类型参数必须是某个类型的父类型。这允许在泛型方法或类中灵活处理不同但兼容的类型。
7. 泛型通配符的限制
泛型通配符(如?、<? extends T>、<? super T>)用于表示未知类型或受限类型。然而,它们也带来了一些限制,如在使用上界通配符时不能添加除null以外的元素,而在使用下界通配符时读取的元素类型只能为Object等。

8. 反射和泛型
Java的反射机制可以在运行时访问类的信息,但由于类型擦除,反射无法直接获取泛型类型参数的具体信息。这要求开发者在使用反射和泛型时需要特别注意类型安全和类型转换的问题。

Read more

AI小说创作神器:5分钟快速部署本地自动写作平台

AI小说创作神器:5分钟快速部署本地自动写作平台 【免费下载链接】AI_NovelGenerator使用ai生成多章节的长篇小说,自动衔接上下文、伏笔 项目地址: https://gitcode.com/GitHub_Trending/ai/AI_NovelGenerator 还在为灵感枯竭而烦恼?想拥有一个能自动生成长篇小说的AI助手吗?今天就来教你如何在5分钟内搭建AI_NovelGenerator,开启AI辅助小说创作之旅。这款开源工具能够自动衔接上下文、埋设伏笔,让你的创作效率提升10倍! 🚀 环境准备:零基础也能轻松搞定 Python环境检查与安装 首先确保你的电脑已安装Python 3.9或更高版本。打开命令行输入以下命令检查Python版本: python --version 如果显示版本号低于3.9,请前往Python官网下载最新版本。推荐使用Python 3.10-3.12,这些版本兼容性最佳。 获取AI模型API密钥 你需要一个有效的API密钥来连接AI模型。支持多种服务商: * OpenAI系列(GPT-4、GPT-3.5等) * D

By Ne0inhk
【大模型】使用llamafactory 训练 qwen2.5-VL 的目标检测任务

【大模型】使用llamafactory 训练 qwen2.5-VL 的目标检测任务

前言 截止到目前,目标检测的功能还是yolo模型落地性更强。但大模型也已经全面开花,所以也尝试下使用大模型来完成目标检测的训练,看看其效果如何,看看它在目标检测上有怎样的优势。 本次选用qwen2.5-VL,一开始使用github上阅读性强的工程训练,总觉得差些意思。 于是决定自己手搓个大模型训练推理工程,emm…,预测效果也是差强人意。 兜兜转转还是使用个高star的工程,先跑通得到想要的效果。后面再阅读核心代码掌握大模型训练过程吧 一 llama factory的部署 【大模型】LLaMA-Factory的环境配置、微调模型与测试 的第二章节,已经介绍了工程安装步骤,整个过程比较流畅。(其余章节介绍了在文本上的大模型微调的过程) 二 llama factory的配置文件介绍 github上给出的训练命令: 以此为切入点,来看看训练自己模型时需要关注的内容,这里先陈列出examples/qwen2_5vl_lora_sft.yamldata/mllm_demo.jsonl2.1 配置文件 【qwen2_5vl_lora_sft.yaml】 该文件中配置了大模型训练过

By Ne0inhk
“AI痕迹太重怎么办?”15个提示词教你降低AIGC率,让写作更像人!

“AI痕迹太重怎么办?”15个提示词教你降低AIGC率,让写作更像人!

还在被AIGC率检测卡住?写得再好,也逃不过“AI痕迹”?别急,这篇文章教你15条最实用的“人类化”提示词,让你的写作摆脱机器人味,一键降重过检! 🧠 为什么你写的AI文章“看起来就像AI写的”? 在很多AIGC检测系统中,比如新版知网、Turnitin、Grammarly、GPTZero等,AI生成内容往往因为这些特征而中招: * 表达过于标准、学境思源,结构死板(比如“引言-三点论证-结尾”的模板) * 用词中性均衡,一键生成,缺乏语气变化 * 没有细节、论文初稿,acaids.com。比喻或非逻辑性插话 * 引用来源少或太“教科书式” * 缺乏真实感和主观思维 这就导致了一个问题:AI写得虽然通顺,但“太工整”,反而容易被机器识别成AI! 🛠️ 如何让AI帮你“写得不像AI”?15个逆转提示词来了! 别再单靠“降重工具”打补丁。更聪明的做法是——从源头开始用“降AIGC率提示词”来让AI写得更像人。

By Ne0inhk

无需任何拓展Copilot接入第三方OpenAI接口教程

禁止搬运,转载需标明本文链接 省流:修改"C:\Users\你的用户名称\.vscode\extensions\github.copilot-chat-0.35.0\package.json"中的"when": "productQualityType != 'stable'"为"when": "productQualityType == 'stable'",即可在copilot添加支持openAI的第三方接口 我在寻找怎么让copilot接入第三方接口的时候,通过别人的贴子(长期有效)接入第三方 OpenAI 兼容模型到 GitHub Copilot-ZEEKLOG博客发现了官方的讨论Add custom OpenAI endpoint configuration

By Ne0inhk