使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
目录
day057 泛型类型的继承规则、通配符类型(通配符概念、通配符的超类限定、无限定通配符、通配符捕获)
1.泛型类型的继承规则
2.通配符概念
3.通配符的超类型限定
4.无限定通配符
5.通配符捕获
在使用泛型类时,需要了解一些有关继承和子类型的准则。下面先从许多程序员感觉不太直观的情况开始。考虑一个类和一个子类,如 Employee 和 Manager。Pair
ManagerD topHonchos=. .
Pair result=ArrayAlg.ininmax(topHonchos);//Error
minmax 方法返回 Pair
Pai r managerBuddies = new Pairo(ceo, cfo);
Pair employeeBuddies = managerBuddies; // illegal, but suppose it wasn't
employeeBuddies.setFirst(1owlyEmployee);
然而,最后一句是合法的。但是 employeeBuddies和 managerBuddies引用了同样的对象。现在将 CFO和 一 个 普 通 员 工 组 成 一 对,这 对 于 Pair
永远可以将参数化类型转换为一个原始类型。例如,PaiKEmployee>是原始类型Pair的一个子类型。在与遗留代码衔接时,这个转换非常必要。转换成原始类型之后会产生类型错误吗?很遗憾,会!看一看下面这个示例:
Pair managerBuddies = new Pairo(ceo, cfo);
Pair rawBuddies = managerBuddies; //OK
rawBuddies.setFirst(new File(". . .")); //only a compile-time warning
听起来有点吓人。但是,请记住现在的状况不会再比旧版 Java的情况糟糕。虚拟机的安全性还没有到生死攸关的程度。当使用 getFirst获得外来对象并赋给 Manager 变量时,与通常一样,会抛出 ClassCastException异常。这里失去的只是泛型程序设计提供的附加安全性。
最后,泛型类可以扩展或实现其他的泛型类。就这一点而言,与普通的类没有什么区别。例如,ArrayList
通配符类型中,允许类型参数变化。例如,通配符类型
Pair extends Employee 〉
表示任何泛型 Pair 类型,它的类型参数是 Employee 的子类,如 Pair
public static void printBuddies(Pairp)
{
Employee first = p.getFirst();
Employee second = p.getSecond();
Systefn.out.println(first.getName()+ " and " +second.getName()+" are buddies.");
}
正如前面讲到的,不能将 Pair
public static void printBuddies(Pair extends Eiployee> p)
类型 Pair
使用通配符会通过 PaiK?extendsEmployee>的引用破坏 Pair
Pair managerBuddies = new PairoCceo, cfo);
Pair extends Employee> wildcardBuddies = managerBuddies; //OK
wi1dcardBuddies.setFirst(1owlyEnployee);//compile-time error
这可能不会引起破坏。对 setFirst 的调用有一个类型错误。要了解其中的缘由,请仔细看一看类型 Pair
? extends Employee getFirst()
void setFirst(? extends Employee)
这样将不可能调用setFirst方法。编译器只知道需要某个Employee的子类型,但不知道具体是什么类型。它拒绝传递任何特定的类型。毕竟?不能用来匹配。
使用getFirst就不存在这个问题:将getFirst的返回值赋给一个Employee的引用完全合法。这就是引人有限定的通配符的关键之处。现在已经有办法区分安全的访问器方法和不安全的更改器方法了。
通配符限定与类型变量限定十分类似,但是,还有一个附加的能力,即可以指定一个超类型限定(supertypebound), 如下所亦:
? super Manager
这个通配符限制为 Manager 的所有超类型。(已有的 super关键字十分准确地描述了这种联系,这一点令人感到非常欣慰。)为什么要这样做呢? 带有超类型限定的通配符的行为与之前介绍的相反。可以为方法提供参数,但不能使用返回值。例如,Pair有方法
void setFirst(?super Manager)
? super Manager getFirst()
这不是真正的 Java语法,但是可以看出编译器知道什么。编译器无法知道 setFirst方法的具体类型,因此调用这个方法时不能接受类型为 Employee或 Object 的参数。只能传递Manager 类型的对象,或者某个子类型(如 Executive) 对象。另外,如果调用 getFirst, 不能保证返回对象的类型。只能把它赋给一个 Object。
下面是一个典型的示例。有一个经理的数组,并且想把奖金最高和最低的经理放在一个Pair 对象中。Pair 的类型是什么? 在这里,Pair
public static void minmaxBonus(Manager[] a, Pair super Manager> result)
{
if (a.length == 0) return;
Manager rain = a[0];
Manager max = a[0];
for (int i* 1 ; i < a.length; i++)
{
if (min.getBonusO > a[i].getBonus()) rain = a[i];
if (max.getBonusO < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
直观地讲,带有超类型限定的通配符可以向泛型对象写人,带有子类型限定的通配符可以从泛型对象读取。
下面是超类型限定的另一种应用。Comparable接口本身就是一个泛型类型。声明如下:
public interface Comparable
{
public int compareTo(T other);
}
在此,类型变量指示了 other 参数的类型。例如,String类实现 Comparable
public int compareTo(String other)
很好,显式的参数有一个正确的类型。接口是一个泛型接口之前,other 是一个 Object,并且这个方法的实现需要强制类型转换。由于 Comparable是一个泛型类型,也许可以把 ArrayAIg类的 min方法做得更好一些 ?可以这样声明:
public static >T min(T[] a)
看起来,这样写比只使用T extentsComparable更彻底,并且对许多类来讲,工作得更好。例如,如果计算一个 String数组的最小值,T就是 String类型的,而 String是Comparable
public static > T min(T[] a) ...
现在 compareTo方法写成
int compareTo(? super T)
有可能被声明为使用类型 T 的对象,也有可能使用 T 的超类型(如当 T 是 LocalDate, T的一个子类型)。无论如何,传递一个 T类型的对象给 compareTo方法都是安全的。对于初学者来说, 还可以使用无限定的通配符,例如,Pair>。初看起来,这好像与原始的 Pair 类型一样。实际上,有很大的不同。类型 Pair>有以下方法: getFirst 的返回值只能赋给一个 Object。setFirst 方法不能被调用,甚至不能用 Object 调用。Pair >和 Pair 本质的不同在于:可以用任意 Object 对象调用原始 Pair 类的 setObject方法。 为什么要使用这样脆弱的类型?它对于许多简单的操作非常有用。例如,下面这个方法将用来测试一个pair是否包含一个mill引用,它不需要实际的类型。 通过将 hasNulls 转换成泛型方法,可以避免使用通配符类型: 但是带有通配符的版本可读性更强。 编写一个交换成对元素的方法: 通配符不是类型变量,因此,不能在编写代码中使用“?”作为一种类型。也就是说,下述代码是非法的: 这是一个问题,因为在交换的时候必须临时保存第一个元素。幸运的是,这个问题有一个有趣的解决方案。我们可以写一个辅助方法swapHelper,如下所示: 注意,swapHelper 是一个泛型方法,而 swap不是,它具有固定的 Pair>类型的参数。现在可以由 swap调用 swapHelper: 在这种情况下,swapHelper方法的参数 T 捕获通配符。它不知道是哪种类型的通配符,但是,这是一个明确的类型,并且 在这里,通配符捕获机制是不可避免的。通配符捕获只有在有许多限制的情况下才是合法的。编译器必须能够确信通配符表达的是单个、确定的类型。例如,ArrayList 下面的程序将前几面讨论的各种方法综合在一起。
4.无限定通配符
? getFirst()
void setFirst(?)
public static boolean hasNulls(Pair>p)
{
return p.getFirstO = null || p.getSecondO = null;
}
public static
5.通配符捕获
public static void swap(Pair>p)
? t=p.getFirst();//Error
p.setFirst(p.getSecond());
p.setSecond(t);
public static
public static void swap(Pair> p) { swapHelper(p); }
public static void maxminBonus(Manager ] a, Pair super Manager> result)
{
minmaxBonus(a, result);
PairAlg.swap(result); //OK
swapHel per captures wildcard type
}
/**
*@author zzehao
*/
public class PairTest3
{
public static void main(String[] args)
{
Manager ceo=new Manager("Gus Greedy",800000,2003,12,15);
Manager cfo=new Manager("Sid Sneaky",600000,2003,12,15);
Pair