六十二、探讨泛型的高级特性:通配符、反射与泛型的结合使用
yuyutoo 2024-10-12 01:23 1 浏览 0 评论
泛型的高级特性如通配符类型、泛型与数组的结合使用以及泛型与反射的结合,进一步拓宽了泛型的应用场景,增强了代码的通用性与表达能力。
通配符类型(?、? extends Type、? super Type)
通配符是泛型中的一个核心概念,允许以更加灵活的方式处理参数化类型。
主要有三种形式:
通配符 | 形式 | 描述 |
无界 通配符 | (?) | 表示可以接受任何类型的泛型参数。在不需要具体类型信息或希望接受多种泛型参数类型时非常有用。例如,List<?> 可以指向任何类型的 List。 |
上界 通配符 | (? extends Type) | 表示只能接受类型 Type 或其子类作为泛型参数。这在方法需要向集合中读取元素但不添加新元素时非常有用,确保类型的安全性。 |
下界 通配符 | (? super Type) | 接受类型 Type 或其超类作为泛型参数。适用于需要向集合中添加元素而不关心具体类型,同时可能从集合中读取到 Type 类型或其父类型的情况。 |
无界通配符(?)案例
假设我们有一个方法,需要打印出列表中所有元素的值,但不知道这个列表的元素具体是什么类型。为了编写一个通用的打印方法,可以使用无界通配符。
import java.util.List;
public class GenericPrintExample {
// 使用无界通配符来定义一个可以接收任何类型列表的打印方法
public static void printList(List<?> list) {
for (Object item : list) {
// 因为不知道具体的类型,所以这里只能将元素当作Object类型处理
// 可以通过调用toString()方法获取元素的字符串表示
System.out.println(item.toString());
}
}
public static void main(String[] args) {
// 创建一个Integer类型的列表
List<Integer> intList = java.util.Arrays.asList(1, 2, 3, 4, 5);
printList(intList); // 调用printList方法,打印整数列表
// 创建一个String类型的列表
List<String> stringList = java.util.Arrays.asList("a", "b", "c", "d", "e");
printList(stringList); // 调用printList方法,打印字符串列表
}
}
在上面的代码中,定义一个printList方法,接受一个类型为List<?>的参数。这里的?表示任意类型,所以这个方法可以接受任何类型的列表作为参数。在方法内部,遍历列表并打印出每个元素的值。由于不知道具体的类型,所以这里只能将元素当作Object类型处理,并调用toString()方法获取元素的字符串表示。
在main方法中,创建一个Integer类型的列表和一个String类型的列表,并分别调用printList方法来打印。printList方法使用无界通配符,可以接受这两种类型的列表作为参数,并成功打印出它们的值。
上界通配符(? extends Type)案例
假设有一个方法,需要接收一个动物(Animal)类型的列表,但列表中的元素可能是动物的具体子类(如Dog或Cat)。由于不确定具体的子类类型,但可以确定它们都是Animal的子类,因此可以使用上界通配符来定义这个方法。
import java.util.List;
class Animal {
public void makeSound() {
System.out.println("The animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("The dog barks");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("The cat meows");
}
}
public class UpperBoundWildcardExample {
// 使用上界通配符来定义一个可以接收Animal类型或其子类类型列表的方法
public static void makeAnimalSounds(List<? extends Animal> animals) {
for (Animal animal : animals) {
// 因为类型参数是Animal或其子类,所以这里可以直接调用Animal的方法
animal.makeSound();
}
}
public static void main(String[] args) {
// 创建一个Dog类型的列表
List<Dog> dogList = java.util.Arrays.asList(new Dog(), new Dog());
makeAnimalSounds(dogList); // 调用makeAnimalSounds方法,打印Dog的声音
// 创建一个Cat类型的列表
List<Cat> catList = java.util.Arrays.asList(new Cat(), new Cat());
makeAnimalSounds(catList); // 调用makeAnimalSounds方法,打印Cat的声音
// 注意:我们不能向animals列表中添加元素,因为编译器不知道具体的类型
// animals.add(new Dog()); // 这会编译错误
}
}
在上面的代码中,定义一个Animal类以及它的两个子类Dog和Cat。然后,定义了一个makeAnimalSounds方法,接受一个类型为List<? extends Animal>的参数。这里的? extends Animal表示列表中的元素类型是Animal或其子类。在方法内部,可以安全地调用Animal类的方法,因为列表中的所有元素都保证是Animal或其子类。
在main方法中,创建了一个Dog类型的列表和一个Cat类型的列表,并分别调用makeAnimalSounds方法来打印它们的声音。由于makeAnimalSounds方法使用了上界通配符,所以可以接受这两种类型的列表作为参数。但是,由于编译器不知道列表中的具体类型,所以不能向该列表中添加元素(如注释中的animals.add(new Dog());),这会导致编译错误。
下界通配符(? super Type)案例
假设有一个方法,用于将一个特定类型的元素添加到一个集合中,但不知道这个集合的具体类型,只知道它能够存储想要添加的元素类型或其父类型。为编写这样的方法,可以使用下界通配符。
import java.util.Collection;
class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}
public class LowerBoundWildcardExample {
// 使用下界通配符来定义一个可以向集合中添加Fruit或其子类元素的方法
public static void addFruits(Collection<? super Apple> collection) {
collection.add(new Apple()); // 可以添加Apple,因为它是集合类型的具体类型或其子类
// collection.add(new Orange()); // 这可能会编译错误,取决于集合的实际类型
}
public static void main(String[] args) {
// 创建一个可以存储Fruit的集合
Collection<Fruit> fruitCollection = new java.util.ArrayList<>();
addFruits(fruitCollection); // 可以,因为Fruit是Apple的父类型
// 创建一个只能存储Apple的集合
Collection<Apple> appleCollection = new java.util.ArrayList<>();
addFruits(appleCollection); // 也可以,因为集合的实际类型就是Apple
// 创建一个只能存储Orange的集合
// 注意:这个集合不能传递给addFruits方法,因为Orange不是Apple的父类型
// Collection<Orange> orangeCollection = new ArrayList<>();
// addFruits(orangeCollection); // 这会编译错误
// 现在fruitCollection和appleCollection都包含至少一个Apple实例
}
}
在上面的代码中,定义一个Fruit类以及它的两个子类Apple和Orange。然后,定义了一个addFruits方法,接受一个类型为Collection<? super Apple>的参数。这里的? super Apple表示集合中的元素类型是Apple或其父类型。在方法内部,可以安全地向集合中添加Apple实例,因为集合保证能够存储Apple类型或其父类型的元素。
在main方法中,创建了一个可以存储Fruit的集合和一个只能存储Apple的集合。这两个集合都可以传递给addFruits方法,因为它们的类型都是Apple的父类型或Apple本身。但是,如果我们尝试传递一个只能存储Orange的集合给addFruits方法,那么编译器会报错,因为Orange不是Apple的父类型。
泛型与反射的结合使用
反射是Java中一种强大的工具,用于在运行时动态获取和操作类的信息。当泛型遇上反射,可以解锁更多高级功能,如动态创建泛型实例、访问泛型类型信息等。由于Java的类型擦除机制,直接通过反射获取泛型类型参数是不可能的,但通过一些技巧可以间接实现。
示例:
假设有一个需求,需要编写一个通用的工厂方法,该方法能够根据传入的类型参数创建对象实例,并根据提供的属性名和对应的值来设置对象的属性。由于泛型信息在编译后会被擦除,无法直接通过泛型获取类型信息,这时就需要借助反射来动态操作。
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
/**
* 定义一个简单的实体类
*/
class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class GenericFactory {
/**
* 泛型方法,根据类型T创建对象实例,并根据属性Map设置属性值
* @param clazz 类型T的Class对象
* @param fieldValues 属性名和对应值的映射
* @param <T> 泛型类型
* @return 实例化的对象
*/
public static <T> T createInstanceWithFields(Class<T> clazz, Map<String, Object> fieldValues)
throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 创建对象实例
T instance = clazz.getDeclaredConstructor().newInstance();
// 遍历属性映射,使用反射设置属性值
for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
// 获取字段
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); // 允许访问私有字段
// 设置字段值
field.set(instance, fieldValue);
}
return instance;
}
}
public class GenericFactoryMain {
public static void main(String[] args)
throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Map<String, Object> userFields = Map.of("name", "Alice", "age", 30);
User user = GenericFactory.createInstanceWithFields(User.class, userFields);
System.out.println(user.getName()); // 输出: Alice
System.out.println(user.getAge()); // 输出: 30
}
}
泛型与数组的结合使用
因为数组的协变性与泛型的不变性冲突,导致泛型和数组在某些方面是互斥的。但在实际应用中,两者仍可以巧妙结合。例如,可能需要创建一个可以持有某种类型数组的泛型类或方法。尽管直接创建泛型数组是受限的,但可以通过以下方式间接实现
示例:使用泛型方法和反射来遍历和操作数组:
import java.lang.reflect.Array;
public class GenericArrayExample {
// 泛型方法,用于打印数组内容
public static <T> void printArray(T[] array) {
for (T item : array) {
System.out.print(item + " ");
}
System.out.println();
}
// 泛型方法,用于创建一个指定类型和长度的数组
@SuppressWarnings("unchecked")
public static <T> T[] createArray(Class<T> clazz, int length) {
return (T[]) Array.newInstance(clazz, length);
}
public static void main(String[] args) {
// 使用泛型方法创建并初始化一个Integer数组
Integer[] intArray = createArray(Integer.class, 5);
for (int i = 0; i < intArray.length; i++) {
intArray[i] = i;
}
// 使用泛型方法打印数组内容
printArray(intArray);
// 使用泛型方法创建并初始化一个String数组
String[] stringArray = createArray(String.class, 3);
stringArray[0] = "Hello";
stringArray[1] = "World";
stringArray[2] = "!";
// 使用泛型方法打印数组内容
printArray(stringArray);
}
}
在这个例子中,我们定义了两个泛型方法:
- printArray:该方法接受一个泛型数组作为参数,并使用增强型for循环遍历并打印数组中的每个元素。
- createArray:该方法使用Java的反射API中的Array.newInstance方法来创建一个指定类型和长度的数组。注意这里使用了@SuppressWarnings("unchecked")注解来抑制编译器关于未检查类型转换的警告,因为在运行时进行了类型转换。
在main方法中,分别使用createArray方法创建了一个Integer数组和一个String数组,并使用printArray方法打印它们的内容。这样,就能够在不直接创建泛型数组的情况下,使用泛型方法来处理数组。
相关推荐
- MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库
-
前言:本文章是在同处局域网内的两台windows电脑,且MySQL是5.5以上版本下进行的一主多从同步配置,并且使用的是集成环境工具PHPStudy为例。最后就是ThinkPHP5的分布式的连接,读写...
- thinkphp5多语言怎么切换(thinkphp5.1视频教程)
-
thinkphp5多语言进行切换的步骤:第一步,在配置文件中开启多语言配置。第二步,创建多语言目录。相关推荐:《ThinkPHP教程》第三步,编写语言包。视图代码:控制器代码:效果如下:以上就是thi...
- 基于 ThinkPHP5 + Bootstrap 的后台开发框架 FastAdmin
-
FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。主要特性基于Auth验证的权限管理系统支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置支持单...
- Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示
-
本文实例讲述了Thinkphp5.0框架实现控制器向视图view赋值及视图view取值操作。分享给大家供大家参考,具体如下:Thinkphp5.0控制器向视图view的赋值方式一(使用fetch()方...
- thinkphp5实现简单评论回复功能(php评论回复功能源码下载)
-
由于之前写评论回复都是使用第三方插件:畅言所以也就没什么动手,现在证号在开发一个小的项目,所以就自己动手写评论回复,没写过还真不知道评论回复功能听着简单,但仔细研究起来却无法自拔,由于用户量少,所以...
- ThinkPHP框架——实现定时任务,定时更新、清理数据
-
大家好,我是小蜗牛,今天给大家分享一下,如何用ThinkPHP5.1.*版本实现定时任务,例如凌晨12点更新数据、每隔10秒检测过期会员、每隔几分钟发送请求保证ip的活性等本次分享,主要用到一个名为E...
- BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统
-
BeyongCms内容管理系统(简称BeyongCms)BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统,适用于企业Cms,个人站长等,针对移动App、小程序优化;提供完善简...
- YimaoAdminv3企业建站系统,使用 thinkphp5.1.27 + mysql 开发
-
介绍YimaoAdminv3.0.0企业建站系统,使用thinkphp5.1.27+mysql开发。php要求5.6以上版本,推荐使用5.6,7.0,7.1,扩展(curl,...
- ThinkAdmin-V5开发笔记(thinkpad做开发)
-
前言为了快速开发一款小程序管理后台,在众多的php开源后台中,最终选择了基于thinkphp5的,轻量级的thinkadmin系统,进行二次开发。该系统支持php7。文档地址ThinkAdmin-V5...
- thinkphp5.0.9预处理导致的sql注入复现与详细分析
-
复现先搭建thinkphp5.0.9环境...
- thinkphp5出现500错误怎么办(thinkphp页面错误)
-
thinkphp5出现500错误,如下图所示:相关推荐:《ThinkPHP教程》require():open_basedirrestrictionineffect.File(/home/ww...
- Thinkphp5.0极速搭建restful风格接口层
-
下面是基于ThinkPHPV5.0RC4框架,以restful风格完成的新闻查询(get)、新闻增加(post)、新闻修改(put)、新闻删除(delete)等server接口层。1、下载Thin...
- 基于ThinkPHP5.1.34 LTS开发的快速开发框架DolphinPHP
-
DophinPHP(海豚PHP)是一个基于ThinkPHP5.1.34LTS开发的一套开源PHP快速开发框架,DophinPHP秉承极简、极速、极致的开发理念,为开发集成了基于数据-角色的权限管理机...
- ThinkPHP5.*远程代码执行高危漏洞手工与升级修复解决方法
-
漏洞描述由于ThinkPHP5框架对控制器名没有进行足够的安全检测,导致在没有开启强制路由的情况下,黑客构造特定的请求,可直接GetWebShell。漏洞评级严重影响版本ThinkPHP5.0系列...
- Thinkphp5代码执行学习(thinkphp 教程)
-
Thinkphp5代码执行学习缓存类RCE版本5.0.0<=ThinkPHP5<=5.0.10Tp框架搭建环境搭建测试payload...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库
- thinkphp5多语言怎么切换(thinkphp5.1视频教程)
- 基于 ThinkPHP5 + Bootstrap 的后台开发框架 FastAdmin
- Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示
- thinkphp5实现简单评论回复功能(php评论回复功能源码下载)
- ThinkPHP框架——实现定时任务,定时更新、清理数据
- BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统
- YimaoAdminv3企业建站系统,使用 thinkphp5.1.27 + mysql 开发
- ThinkAdmin-V5开发笔记(thinkpad做开发)
- thinkphp5.0.9预处理导致的sql注入复现与详细分析
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)