Java泛型通配符

来源:互联网 发布:strcontains php 编辑:程序博客网 时间:2024/05/17 21:07

1. 泛型通配符要解决的一个常见问题

首先明确一个问题。假设Manager继承Employee,Info<Manager> 和Info<Employee>直接却没有任何关系。所以我们如果定义了如下函数:

public void printInfo(Info<Employee> info) {   ...}

我们并不能用Info<manager> managerInfo; printInfo(managerInfo); 这样的调用。

如果我们希望定义一个函数,任何在Employee继承线上的类型都可以被调用,那么我们需要使用通配符‘?’

2. 通配符的概念

我们将‘?’称为通配符,应将其理解为‘未知类型’。对于public void printInfo(Info<?> info) 我们应将其解读为‘答应一个未知类型的Info’

3. ? extends X

对于未知类型添加了一个限制,就是这个未知类型要继承自X。

对于这个限制,我们分两方面考虑。

a. 当通配符作为类的类型参数:

public static void printInfo(Info<? extends Employee> info) {...}

对这个case我们要考虑的是如何传入一个合适的info参数。我们传入参数的基本原则就是可以传入一个子类对象给一个父类的声明。所以问题就变为弄清楚'? extends Employee'的继承树。

这里直接给出:Pair<Manager> =====

                                                              |====> Pair<? extends Employee> ====> Pair

                         Pair<Employee> ====

所以我们可以用Pair<Manager> 或 Pair<Employee>的实例作为函数的调用参数。

b.当通配符本身作为类型参数

我们要将? extends Employee作为一个整体理解,首先? extends Employee代表一种不确定类型,我们称这个类型为X,这个X类型又有一个确定性,就是他是Employee或其子类。

public class Info<T>{private T getDetail() {...}private void setDetail(T detail) {...}}

假设我们实例化Info<? extends Employee> employeeInfo = new Info<Manager>(); 根据上一点的分析,这个赋值是OK的。

(1)employeeInfo.getDetail()的返回值类型为X,X继承Employee。一个函数的返回值类型只要确定父类,对于这个函数来说就确定了。所以我们可以将getDetail的解读为返回值类型为Employee。

所以Employee e = employeeInfo.getDetail(); //没问题

Manager m = employeeInfo.getDetail(); // error, 类型转化错误,因为这个返回值类型为Employee

(2)employeeInfo.setDetail(...) 这里赋予任何值非NULL值都会出错。还是和上一点一样的分析,用employeeInfo调用setDetail以后,setDetail变为setDetail(X),函数声明中的X类型不确定,只知道是继承自Employee,但不确定是不是自更小的类,比如Manager。函数赋值的基本原则”可以将子类实例赋予父类声明“,变得无法可依,因为你不知道声明的父类是什么类型。如果他是Manager,那么你就不可以用Employee的实例赋予,如果他是Executive,那么你就不可以用Manager的实例赋予。

4. ? super X

a. 当通配符作为类的类型参数:

public static void printInfo(Info<? super Manager> info) {...}

同样,对这个case我们要考虑的是如何传入一个合适的info参数。我们传入参数的基本原则就是可以传入一个子类对象给一个父类的声明。所以问题就变为弄清楚'? extends Employee'的继承树。

Pair<Employee> ====

                                   |=====> Pair<? super Manager> =====> Pair

Pair<Object> =======     

所以我们可以用Pair<Employee> 或 Pair<Object>传入printInfo的参数


b. 当通配符本身作为参数:

同样的分析,将? super Manager 作为整体分析,记为X,X首先是不确定的,super引入了一个确定性,就是X必须是Manager的父类。

(1)getDetail() 返回值类型为X, X是Manager的父类,但不知道是哪个父类,这样一来函数就不知道该返回哪个父类了,所以getDetail()在这个情况下不可以使用。

(2)setDetail(X) X是Manager的父类,所以在我们向X赋值的时候,传入Manager及其子类就一定没有错。 


原创粉丝点击