一、Service<t> - 泛型类的基本形式
Service<t>是我们定义的一个泛型类,这里的T是一个类型参数,它在类定义时并不确定具体类型,而是在实例化时由使用者指定,示例如下。
public class Service<T> {
private T data;
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
// 使用时指定具体类型
Service<String> stringService = new Service<>();
stringService.setData("Hello"); // 正确
stringService.setData(123); // 编译错误!类型不匹配
关键特点:
类型安全:编译器会在编译期进行类型检查
无需强制类型转换
代码可读性更好
二、Service - 参数化为Object类型
Service 是Service<t>的一个特例,其中类型参数T被具体指定为Object。
Service<Object> objectService = new Service<>();
objectService.setData("String"); // 正确
objectService.setData(123); // 正确
objectService.setData(new Date()); // 正确
// 但是获取时需要强制类型转换
String str = (String) objectService.getData(); // 需要显式转换
关键特点:
可以存储任何类型的对象
失去了编译期类型安全检查的优势
获取数据时需要手动进行类型转换
与原始类型相比,它仍然是参数化类型
三、Service<?> - 通配符类型
Service<?>使用通配符?表示未知类型,这是Java泛型中处理灵活性的重要机制。
// 可以声明,但不能创建实例
Service<?> wildcardService; // 正确
// Service<?> service = new Service<?>(); // 编译错误!
// 通常用于方法参数,表示接受任何类型的Service
public void processService(Service<?> service) {
// 可以从service获取数据,但类型是Object
Object obj = service.getData();
// 不能向service设置数据(null除外)
// service.setData("test"); // 编译错误!
service.setData(null); // 唯一允许的设置
}
关键特点:
表示"某种特定但未知的类型"
主要用于声明和参数,不能直接实例化
只读不写(除了null)
支持上下界通配符:Service<? extends Number>和Service<? super Integer>
四、Service - 原始类型(Raw Type)
直接使用Service而不带任何泛型参数,这就是所谓的原始类型。这是为了向后兼容Java 5之前的代码而保留的特性。
// 原始类型 - 完全不使用泛型
Service rawService = new Service();
rawService.setData("String"); // 正确,但有警告
rawService.setData(123); // 正确,但有警告
// 获取数据时返回Object,需要强制转换
String str = (String) rawService.getData(); // 运行时可能抛出ClassCastException
关键特点:
完全绕过泛型类型检查
编译器会生成"unchecked"警告
与泛型之前的Java代码兼容
不推荐在现代Java代码中使用
总结:
| 类型 | 类型安全 | 是否需要类型转换 | 可读性 | 使用场景 |
|---|---|---|---|---|
| Service | 编译期检查 | 否 | 优秀 | 明确知道具体类型的场景 |
| Service | 部分安全 | 是 | 一般 | 需要存储多种类型但保留泛型结构 |
| Service | 部分安全 | 是 | 良好 | 方法参数、灵活接受多种泛型实例 |
| Service | 不安全 | 是 | 差 | 兼容旧代码,不推荐在新代码中使用 |