博客
关于我
java_泛型结构模型整理
阅读量:248 次
发布时间:2019-03-01

本文共 3471 字,大约阅读时间需要 11 分钟。

官方教程:Java泛型类型通配符详解

在编写Java程序时,我们有时需要处理不同类型的对象,但又希望能够在不暴露具体类型的情况下进行操作。Java 提供了泛型类型通配符,用于实现这种需求。本文将详细介绍类型通配符的使用方法及其相关限制。


从特殊情况抽象出一般情况

在编写泛型类时,通常会遇到以下场景:

  • 类型参数的具体类型未知。
  • 需要处理不同类型的对象,但又希望保证其具有一些共同的属性或方法。

示例

假设我们有一个抽象类 Shape,其中定义了一个抽象方法 draw(Canvas c)。然后,具体实现该方法的类有 CircleRectangle。我们希望创建一个 Canvas 类,其中有一个 drawAll(List shapes) 方法,能够接受任何类型的 Shape 实例。

问题

原始代码中,drawAll() 方法的参数类型被限定为 List<Shape>,因此只能接受 Shape 类型的列表。然而,实际上,我们希望它能够接受任何 Shape 的子类类型的列表,例如 List<Circle>List<Rectangle>

解决方案

我们可以将 List 的通配符修改为 List<? extends Shape>。这样,drawAll() 方法就可以接受任何类型的 Shape 实例的列表。这种通配符被称为上界通配符


类型通配符的使用特点

1. 上界通配符(? extends Type

上界通配符允许我们指定通配符的上界,即通配符可以接受的具体类型及其子类。例如:

List
shapes = new ArrayList<>();
shapes.add(new Circle());
shapes.add(new Rectangle());

在这种情况下,shapes 列表可以包含 Shape 类型及其任意子类(如 CircleRectangle)。

2. 下界通配符(? super Type

下界通配符允许我们指定通配符的下界,即通配符只能接受某个具体类型或其父类。例如:

List
numbers = new ArrayList<>();
numbers.add(new Integer(10));
numbers.add(new Double(3.14));

在这种情况下,numbers 列表可以包含 Number 类型及其父类(如 Object)。

3. 无限定通配符(?

无限定通配符允许通配符接受任何类型,但在进行写操作时会带来潜在的类型安全风险。例如:

List
list = new ArrayList<>();
list.add(new Object());
Object obj = list.get(0);

这里,obj 的类型会被推断为 Object,但无法确保其实际类型是否与 List 中的元素类型一致。


类型通配符的实际应用

示例 1:使用上界通配符

考虑一个 Canvas 类,其中有一个 drawAll(List<? extends Shape> shapes) 方法。我们可以将任何类型的 Shape 实例的列表传递给这个方法:

List
circles = new ArrayList<>();
circles.add(new Circle());
drawAll(circles);

此时,shapes 的类型被推断为 List<Circle>,满足 Shape 的上界要求。

示例 2:使用下界通配符

考虑一个 Census 类,其中有一个 addRegistry(Map<String, ? extends Person> registry) 方法。我们可以将任意类型的 Person 实例的映射传递给这个方法:

Map
drivers = new HashMap<>();
drivers.put("张三", new Driver());
addRegistry(drivers);

此时,registry 的类型被推断为 Map<String, Driver>,满足 Person 的下界要求。


类型通配符的限制

1. 上界通配符的优点

  • 提供了类型的上界,确保通配符只能接受特定类型及其子类。
  • 在读操作时,可以安全地调用通配符类型的方法。

2. 上界通配符的缺点

  • 在写操作时,不能随意传递任意类型的实例。
  • 需要确保传入的实例类型是通配符类型的子类或本身。

示例

以下代码会导致编译错误:

List
shapes = new ArrayList<>();
shapes.add(new Rectangle());

原因是 RectangleShape 的子类,满足上界要求。但如果尝试传入非 Shape 类型的实例,会导致错误。


实际案例:Pair 类的泛型方法

类定义

public class Pair
{
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
public void setFirst(T first) {
this.first = first;
}
public void setLast(T last) {
this.last = last;
}
}

使用示例

Pair
integerPair = new Pair<>(123, 456);
Pair
stringPair = new Pair<>("test", "test");
integerPair.setFirst(456);
stringPair.setLast("test2");

说明

  • integerPair 的类型是 Pair<Integer>,只能存储 Integer 类型的对象。
  • setFirstsetLast 方法可以传递任意类型的实例,只要它们是目标类型的子类。

类型通配符的安全性

例子:上界通配符

public class TestExtends {
public static void main(String[] args) {
Pair
p = new Pair<>(123, 456);
printGetValues(p);
}
static int add(Pair
p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
static void printGetValues(Pair
p) {
Object obj = p.getFirst();
System.out.println(obj.getClass().getName());
}
}

说明

  • add 方法可以接受任何类型的 Number 实例的 Pair
  • printGetValues 方法只能打印 Pair 中的元素值,无法获取其具体类型。

总结

通过上述案例可以看出,类型通配符在Java 中提供了极大的灵活性。上界通配符允许我们指定通配符的上界,确保通配符只能接受特定类型及其子类。下界通配符则允许我们指定通配符的下界,确保通配符只能接受某个类型及其父类。无限定通配符虽然灵活,但需要谨慎使用,以避免类型安全问题。

在实际开发中,选择合适的通配符类型对于代码的可读性和维护性至关重要。同时,需要注意通配符的边界条件,以确保代码的类型安全。

转载地址:http://bozt.baihongyu.com/

你可能感兴趣的文章
node.js debug在webstrom工具
查看>>
Node.js HTTP模块详解:创建服务器、响应请求与客户端请求
查看>>
Node.js RESTful API如何使用?
查看>>
node.js url模块
查看>>
Node.js Web 模块的各种用法和常见场景
查看>>
Node.js 之 log4js 完全讲解
查看>>
Node.js 函数是什么样的?
查看>>
Node.js 函数计算如何突破启动瓶颈,优化启动速度
查看>>
Node.js 切近实战(七) 之Excel在线(文件&文件组)
查看>>
node.js 初体验
查看>>
Node.js 历史
查看>>
Node.js 在个推的微服务实践:基于容器的一站式命令行工具链
查看>>
Node.js 实现类似于.php,.jsp的服务器页面技术,自动路由
查看>>
Node.js 异步模式浅析
查看>>
node.js 怎么新建一个站点端口
查看>>
Node.js 文件系统的各种用法和常见场景
查看>>
Node.js 模块系统的原理、使用方式和一些常见的应用场景
查看>>
Node.js 的事件循环(Event Loop)详解
查看>>
node.js 简易聊天室
查看>>
Node.js 线程你理解的可能是错的
查看>>