Java泛型详解(下)

九. 泛型类型的继承规则

假设现在有一个类Employee和它的子类Manager

现在问题来了:
Pair<Manager>是Pair<Employee>的子类吗?

答案是:不是

例如,下面的代码将不会编译成功:

Manager[] topHonchos = ...;
Pair<Employee> result = ArrayAlg.minmax(topHonchos); //Error
//minmax方法返回Pair<Manager>, 而不是Pair<Employee>, 这样的赋值是不合法的

也就是说,无论 S 与 T 有什么联系,通常,Pair<S>与Pair<T>没有什么联系
也可以这么理解,无论赋给类型变量的类型之间有什么联系,在泛型类里,这些关系都不存在了

另外,泛型类可以扩展或实现其他的泛型类,例如:ArrayList<T>类实现了List<T>接口,这意味着,一个ArrayList<Manager>可以被转换为一个List<Manager>。但是,一个ArrayList<Manager>不是一个ArrayList<Employee>或List<Employee>

十. 通配符类型

1. 通配符的概念:

假设我们现在需要打印一对员工的方法:

public static void printBuddies(Pair<Employee> p) {
    Employee first = p.getFirst();
    Employee second = p.getSecond();
    System.out.println(first.getName() + "and" + second.getName() + "are buddies.");
}

现在问题来了,Manager虽然不同于普通员工,但他也算是员工,他是员工的子类,但是根据之前说的泛型类继承规则,Pair<Manager>与Pair<Employee>没有任何关系,所以这个方法就不能接受Pair<Manager>类了,难道Manager与普通员工就不能做朋友了吗?不,为了解决这个问题,我们使用通配符类型

public static void printBuddies(Pair<? extends Employee> p)

类型Pair<Manager>是Pair<? extends Employee>的子类型
可以认为,通配符为泛型类之间添加了一层联系,然而这种联系并不纯粹,我们来看一个例子:

Pair<Manager> managerBuddies = new Pair<>(CEO, CFO); //JDK7之后可以省略构造函数的类型变量,由编译器根据语句自己翻译
Pair<? extends Employee> wildcBuddies; //OK
wildcardBuddies.setFirst(lowlyEmployee); //这步就会报错,compile-time error

究其原因,我们来看对于类型Pair<? extends Employee>内部对 setFirst 和 getFirst 方法的调用,它是这样的:

? extends Employee getFirst() //将getFirst返回值赋给一个Employee的引用完全合法
void setFirst(? extends Employee) //出现类型错误,因为编译器只知道需要某个Employee的子类型,但不知道具体是什么类型

2. 通配符的超类型限定

通配符还有一个比较强的功能就是可以指定一个超类,像这样:

? super Manager //super关键字限制通配符为Manager的所有超类(父类)

这样的做法与之前的子类型限定恰好相反。这样可以为方法提供参数(setFirst),但不能使用返回值(getFirst)

void setFirst(? super Manager)
? super Manager getFirst() 

直观的讲,带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取

3. 无限定通配符

还可以使用无限定通配符,例如,Pair<?>,初看起来,这好像与原始的Pair类型一样,实际上却有很大的不同,类型Pair<?>有以下的方法:

? getFirst()
void setFirst(?)

getFisrt的返回值只能赋给一个Object。setFirst方法不能被调用,甚至不能用Object调用。Pair<?>和Pair的本质不同在于:可以用任意Object对象调用原始Pair类的setObject方法

4. 通配符捕获

参考文献:[美]Cay S. Horsimann著 《Java 核心技术 卷I》(第十版)

时间: 2024-09-18 09:24:13

Java泛型详解(下)的相关文章

Java泛型详解_java

1. Why --引入泛型机制的原因     假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小可以改变的Date对象数组,这时我们当然希望能够重用之前写过的那个针对String对象的ArrayList实现.     在Java 5之前,ArrayList的实现大致如下: public class ArrayList { public Object get(int i) { ... }

Java正则表达式详解(下)

详解|正则 三.应用实例 下面我们来看看Jakarta-ORO库的一些应用实例. 3.1 日志文件处理 任务:分析一个Web服务器日志文件,确定每一个用户花在网站上的时间.在典型的BEA WebLogic日志文件中,日志记录的格式如下: 分析这个日志记录,可以发现,要从这个日志文件提取的内容有两项:IP地址和页面访问时间.你可以用分组符号(圆括号)从日志记录提取出IP地址和时间标记. 首先我们来看看IP地址.IP地址有4个字节构成,每一个字节的值在0到255之间,各个字节通过一个句点分隔.因此,

Java泛型详解(上)

一. 什么是泛型 泛型是一种程序设计手段(机制),使用泛型可以让你的代码被很多不同类型的对象所重用,提高代码的重用性,还可以提高代码的可读性和安全性 比如,我们经常使用的ArrayList类,就是一个泛型类,也正因如此,它可以接受很多不同类型的对象 /* 可以根据需要存储不同类型的对象 */ ArrayList<Integer> arraylist = new ArrayList<Integer>(); ArrayList<String> arraylist = new

Java泛型详解

一 概念 1.1 为什么需要泛型?           当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型.因此,取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现"java.lang.ClassCastException"异常.使用泛型就可以解决此类问题. 1.2 什么是泛型?         泛型(Generic type 或者 generics)是对 Java 语

Java 泛型详解

在日常的开发中,我们会看到别人的框架很多地方会使用到泛型,泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法.泛型的类型参数只能是类类型(包括自定义类),不能是简单类型.本篇博客我们就来详细解析一下泛型的知识. 泛型类定义及使用 使用泛型有什么好处呢?首先我们先看一个例子,假设我们有两个类,代码如下: #StringClass  public class Strin

Java虚拟机详解----JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制.   先把本文的目录画一个思维导图:(图的源文件在本文末尾)   一.Java引用的四种状态: 强引用: 用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. 如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,Java

java关键字(详解)

基本类型 1 boolean 布尔型 2 byte 字节型 3 char 字符型 4 double 双精度 5 float 浮点 6 int 整型 7 long 长整型 8 short 短整型 9 null 空 10 true 真 11 false 假 程序控制语句 1 break 跳出中断 2 continue 继续 3 return 返回 4 do 运行 5 while 循环 6 if 如果 7 else 否则 8 for 循环 9 instanceof 实例 10 switch 观察 11

Java NIO 详解(一)

NIO即新的输入输出,这个库是在JDK1.4中才引入的.它在标准java代码中提供了高速的面向块的IO操作. 一.基本概念描述 1.1 I/O简介 I/O即输入输出,是计算机与外界世界的一个借口.IO操作的实际主题是操作系统.在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象一次移动一个字节.流IO负责把对象转换为字节,然后再转换为对象. 关于Java IO相关知识请参考我的另一篇文章:Java IO 详解 1.2 什么是NIO NIO即New

HBase Java API详解

[本文转自HBase Java API详解] HBase是Hadoop的数据库,能够对大数据提供随机.实时读写访问.他是开源的,分布式的,多版本的,面向列的,存储模型. 在讲解的时候我首先给大家讲解一下HBase的整体结构,如下图: HBase Master是服务器负责管理所有的HRegion服务器,HBase Master并不存储HBase服务器的任何数据,HBase逻辑上的表可能会划分为多个HRegion,然后存储在HRegion Server群中,HBase Master Server中存