第18章 泛型
泛型是J2SE 5中新添加的特性。该特性可以说对Java的影响非常大,甚至说翻天覆地的变化也不为过。本章主要对Java的泛型特性进行介绍。主要包括为什么要使用泛型,泛型方法、泛型类的使用以及泛型对Java继承机制的影响。通过本章的学习,读者应该在编程中尽量的使用泛型。
18.1 为什么泛型
这一节主要介绍Java中泛型产生的原因,泛型为Java带来的好处。主要还是通过示例来进行介绍,比较泛型前后编程的不同。通过一个使用泛型来编程的例子以及一个不使用泛型来解决,比较这两种方案,就可以知道泛型的优点了。
18.1.1 什么是泛型
简单来说,泛型就是将类型参数化。类型参数化是指把数据类型也可以作为参数来指定,这样就允许类、接口、方法处理的数据类型被指定为一个参数。
在Java中,所有类的根父类都是Object类,可以通过类型转换来处理各种类型的对象。在泛型出现之前,就是通过这种方式来处理的。这样类型转换的过程是很繁琐的,而且由于Object可以处理任何的类,这种方式会带来许多不安全的隐患。并且只能在程序运行的时候才能表现出来,给系统的安全带来了很多问题。
18.1.2 没有泛型的情况
本小节主要介绍一个没有使用泛型的情况下,如何用一个类来处理多种数据类型。主要分析这种方式的缺点和带来的不方便。在不使用泛型的情况下是用Object类来处理各种不同对象的。
18.1.3 一个泛型的例子
这一小节主要的内容是用泛型来实现上面一节中程序实现的相同的功能。不过在这个例子中使用了泛型来处理。
需要注意的一点是泛型处理的必须是类类型。不能用它来处理int类型或long类型等简单类型,不能把简单类型作为参数传递给类型参数T。
18.2 泛型的使用
前面一节已经介绍了泛型带来的好处,那么如何高效、深入的使用泛型来解决程序编写中遇到的问题就是本节将要介绍的内容。本节的内容主要介绍多个泛型参数、有界类型、类型通配符、泛型方法等内容,主要还是通过一些简单例子来进行说明。
18.2.1 使用两个或多个类型参数
在前面介绍的简单例子中,只使用了一个类型参数。实际上,在泛型类的使用中可以有两个或多个类型参数,只需把它们用逗号隔开就可以了。
18.2.2 类型边界
引入泛型要解决的问题之一就是类型安全问题,现在来看一下这种机制。泛型类的类型参数是可以随便指定的,只要是一种对象类型就可以,这样在有的情况下会带来一些问题。可能程序所设计的泛型类只希望它处理一定范围的类型,这样不仅能带来类型上的安全,而且能增加类型的操作类型,这样会带来许多方便。
18.2.3 通配符使用
泛型的使用会带来一个问题。如果在程序中需要用这个参数化类型作为方法参数,程序的本意是用它来表示另一种参数化类型,如果这种参数与已经指定的类型是不同的,这时就会遇到问题。因为已经指定了类型参数的类型,这样在使用该参数表示另外的类型显然是不行的。
18.2.4 泛型方法
前面介绍的内容都是在泛型类中使用参数化类型来作为参数。实际上在非泛型类中也可以使用泛型,通过泛型来参数化方法的参数。这就是泛型方法。本小节还是通过一个简单的示例来对本部分内容进行介绍。
18.3 泛型层次
在普通的类中,继承是非常重要的一种机制,因此泛型类自然会对继承提供支持。在Java中,泛型类可以是父类也可以是子类。在泛型类的继承机制中,参数类型是通过继承层次类型从子类开始逐层向上传递的。
18.3.1 泛型继承
注意在泛型子类声明中必须指定其父类的类型参数,即使在这个类中并不需要用到T。仍然要在参数列表中指定它。
在GenExtendsDemo创建了两个泛型子类对象。创建对象的过程中会调用的子类和父类的构造函数,并记录了它们的执行顺序。可以看到在构造子类对象的时候,把类型参数传递给了父类。程序的输出结果如下:
泛型父类的构造函数执行
T的类型为:java.lang.Integer
泛型子类的构造函数执行
泛型父类的构造函数执行
T的类型为:java.lang.String
泛型子类的构造函数执行
18.3.2 泛型与非泛型类的继承
在上一节介绍的例子中,泛型的继承仅仅是在泛型类之间的。实际上继承可以是泛型类和非泛型类的关系。简单的来说可以分为两种情况:一种是父类为泛型类,而子类是非泛型类,另一种情况是父类为非泛型类,子类是泛型类。本节将会分别介绍一个例子对其进行说明。
18.3.3 泛型继承中方法的覆盖
泛型并没有违反以前Java的任何特点,而是只带来了一些方便。例如在泛型类中声明的方法中,方法覆盖的机制仍然是存在的,下面的例子说明了这一点。
18.4 小结
本章主要内容介绍了J2SE 5中提供的新特性,泛型。泛型可以说给Java带来了革命性的变化。读者在编写程序的过程中能体会到它带来的种种便利。泛型带来的强大功能不是短短一章能介绍清楚的,读者可以在以后的编程中尽量的使用这种机制,这样才能慢慢体会其带来的好处。