`

Java编程思想(第4版) 之 15.2 简单泛型

阅读更多

15.1 与C++的比较 

一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。

在面向对象编程语言中,多态算是一种泛化机制。例如,你可以将方法的参数类型设为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数。这样的方法更加通用一些,可应用的地方也多一些。在类的内部也是如此,凡是需要说明类型的地方,如果都使用基类,确实能够具备更好的灵活性。但是,考虑到除了final类不能扩展,其他任何类都可以被扩展,所以这种灵活性大多数时候也会有一些性能损耗。

有时候,拘泥于单继承体系,也会使程序受限太多。如果方法的参数是一个接口,而不是一个类,这种限制就放松了许多。因为任何实现了该接口的类都能够满足该方法,这也包括暂时还不存在的类。这就给予客户端程序员一种选择,他可以通过实现一个接口来满足类或方法。因此,接口允许我们快捷地实现类继承,也使我们有机会创建一个新类来做到这一点。

可是有的时候,即便使用了接口,对程序的约束也还是太强了。因为一旦指明了接口,它就要求你的代码必须使用特定的接口。而我们希望达到的目的是编写更通用的代码,要使代码能够应用于“某种不具体的类型”,而不是一个具体的接口或类。

这就是Java SE5的重大变化之一:泛型的概念。泛型实现了参数化类型的概念,使代码可以应用于多种类型。“泛型”这个术语的意思是:“适用于许多许多的类型”。泛型在编程语言中出现时,其最初的目的是希望类或方法能够具备最广泛的表达能力。如何做到这一点呢,正是通过解耦类或方法与所使用的类型之间的约束。稍后你将看到,Java中的泛型并没有这么高的追求,实际上,你可能会质疑,Java中的术语“泛型”是否适合用来描述这一功能。

如果你从未接触过参数化类型机制,那么,在学习了Java中的泛型之后,你会发现,对这门语言而言,泛型确实是一个很有益的补充。在你创建参数化类型的一个实例时,编译器会为你负责转型操作,并且保证类型的正确性。这应该是一个进步。

然而,如果你了解其他语言(例如C++)中的参数化类型机制,你就会发现,有些以前能做到的事情,使用Java的泛型机制却无法做到。使用别人已经构建好的泛型类型会相当容易。但是如果你要自己创建一个泛型实例,就会遇到许多令你吃惊的事情。在本章中,我的任务之一就是向你解释,Java中的泛型是怎样发展成现在这样的。

这并非是说Java的泛型毫无用处。在很多情况下,它们可以使代码更直接更优雅。不过,如果你具备其他语言的经验,而那种语言实现了更纯粹的泛型,那么,Java可能令你失望了。在本章中,我们会介绍Java泛型的优点与局限,希望这能够帮助你更有效地使用Java的这个新功能。

15.1   与C++的比较

Java的设计者曾说过,设计这门语言的灵感主要来自C++。尽管如此,学习Java时,基本上可以不用参考C++。我也是尽力这样做的,除非,与C++的比较能够加深你的理解。

Java中的泛型就需要与C++进行一番比较,理由有二:首先,了解C++模板的某些方面,有助于你理解泛型的基础。同时,非常重要的一点是,你可以了解Java泛型的局限是什么,以及为什么会有这些限制。最终的目的是帮助你理解,Java泛型的边界在哪里。根据我的经验,理解了边界所在,你才能成为程序高手。因为只有知道了某个技术不能做到什么,你才能更好地做到所能做的(部分原因是,不必浪费时间在死胡同里乱转)。

第二个原因是,在Java社区中,人们普遍对C++模板有一种误解,而这种误解可能会误导你,令你在理解泛型的意图时产生偏差。

因此,在本章中会介绍一些C++模板的例子,不过我也会尽量控制它们的篇幅。

 

15.2   简单泛型

有许多原因促成了泛型的出现,而最引人注目的一个原因,就是为了创造容器类。(关于容器类,你可以参考第11章和第17章这两章。)容器,就是存放要使用的对象的地方。数组也是如此,不过与简单的数组相比,容器类更加灵活,具备更多不同的功能。事实上,所有的程序,在运行时都要求你持有一大堆对象,所以,容器类算得上最具重用性的类库之一。

我们先来看看一个只能持有单个对象的类。当然了,这个类可以明确指定其持有的对象的类型:



 

不过,这个类的可重用性就不怎么样了,它无法持有其他类型的任何对象。我们可不希望为碰到的每个类型都编写一个新的类。

在Java SE5之前,我们可以让这个类直接持有Object类型的对象:



 

现在,Holder2可以存储任何类型的对象,在这个例子中,只用了一个Holder2对象,却先后三次存储了三种不同类型的对象。

有些情况下,我们确实希望容器能够同时持有多种类型的对象。但是,通常而言,我们只会使用容器来存储一种类型的对象。泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。

此,与其使用Object,我们更喜欢暂时不指定类型,而是稍后再决定具体使用什么类型。要达到这个目的,需要使用类型参数,用尖括号括住,放在类名后面。然后在使用这个类的时候,再用实际的类型替换此类型参数。在下面的例子中,T就是类型参数:



 

现在,当你创建Holder3对象时,必须指明想持有什么类型的对象,将其置于尖括号内。就像main()中那样。然后,你就只能在Holder3中存入该类型(或其子类,因为多态与泛型不冲突)的对象了。并且,在你从Holder3中取出它持有的对象时,自动地就是正确的类型。

这就是Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。

一般而言,你可以认为泛型与其他的类型差不多,只不过它们碰巧有类型参数罢了。稍后我们会看到,在使用泛型时,我们只需指定它们的名称以及类型参数列表即可。

练习1:(1) 配合typeinfo.pets类库,用Holder3来证明,如果指定Holder3可以持有某个基类类型,那么它也能持有导出类型。

练习2:(1) 创建一个Holder类,使其能够持有具有相同类型的3个对象,并提供相应的读写方法访问这些对象,以及一个可以初始化其持有的3个对象的构造器。

15.2.1   一个元组类库

仅一次方法调用就能返回多个对象,你应该经常需要这样的功能吧。可是return语句只允许返回单个对象,因此,解决办法就是创建一个对象,用它来持有想要返回的多个对象。当然,可以在每次需要的时候,专门创建一个类来完成这样的工作。可是有了泛型,我们就能够一次性地解决该问题,以后再也不用在这个问题上浪费时间了。同时,我们在编译期就能确保类型安全。

这个概念称为元组(tuple),它是将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。(这个概念也称为数据传送对象,或信使。)

通常,元组可以具有任意长度,同时,元组中的对象可以是任意不同的类型。不过,我们希望能够为每一个对象指明其类型,并且从容器中读取出来时,能够得到正确的类型。要处理不同长度的问题,我们需要创建多个不同的元组。下面的程序是一个2维元组,它能够持有两个对象:



 



 

构造器捕获了要存储的对象,而toString()是一个便利函数,用来显示列表中的值。注意,元组隐含地保持了其中元素的次序。

第一次阅读上面的代码时,你也许会想,这不是违反了Java编程的安全性原则吗?first和second应该声明为private,然后提供getFirst()和getSecond()之类的访问方法才对呀?让我们仔细看看这个例子中的安全性:客户端程序可以读取first和second对象,然后可以随心所欲地使用这两个对象。但是,它们却无法将其他值赋予first或second。因为final声明为你买了相同的安全保险,而且这种格式更简洁明了。

还有另一种设计考虑,即你确实希望允许客户端程序员改变first或second所引用的对象。然而,采用以上的形式无疑是更安全的做法,这样的话,如果程序员想要使用具有不同元素的元组,就强制要求他们另外创建一个新的TwoTuple对象。

我们可以利用继承机制实现长度更长的元组。从下面的例子中可以看到,增加类型参数是件很简单的事情:

为了使用元组,你只需定义一个长度适合的元组,将其作为方法的返回值,然后在return语句中创建该元组,并返回即可。

由于有了泛型,你可以很容易地创建元组,令其返回一组任意类型的对象。而你所要做的,只是编写表达式而已。

通过ttsi.first = "there"语句的错误,我们可以看出,final声明确实能够保护public元素,在对象被构造出来之后,声明为final的元素便不能被再赋予其他值了。

在上面的程序中,new表达式确实有点罗嗦。本章稍后会介绍,如何利用泛型方法简化这样的表达式。

练习3:(1) 使用泛型编写一个SixTuple类,并测试它。

练习4:(3) “泛型化”innerclasses/Sequence.java类。

15.2.2   一个堆栈类

接下来我们看一个稍微复杂一点的例子:传统的下推堆栈。在第11章中,我们看到,这个堆栈是作为net.mindview.util.Stack类,用一个LinkedList实现的。在那个例子中,LinkedList本身已经具备了创建堆栈所必需的方法,而Stack可以通过两个泛型的类Stack<T>和LinkedList<T>的组合来创建。在那个示例中,我们可以看出,泛型类型也就是另一种类型罢了(稍候我们会看到一些例外的情况)。

现在我们不用LinkedList,来实现自己的内部链式存储机制。

内部类Node也是一个泛型,它拥有自己的类型参数。

这个例子使用了一个末端哨兵(end sentinel)来判断堆栈何时为空。这个末端哨兵是在构造LinkedStack时创建的。然后,每调用一次push()方法,就会创建一个Node<T>对象,并将其链接到前一个Node<T>对象。当你调用pop()方法时,总是返回top.item,然后丢弃当前top所指的Node<T>,并将top转移到下一个Node<T>,除非你已经碰到了末端哨兵,这时候就不再移动top了。如果已经到了末端,客户端程序还继续调用pop()方法,它只能得到null,说明堆栈已经空了。

练习5:(2) 移除Node类上的类型参数,并修改LinkedStack.java的代码,证明内部类可以访问其外部类的类型参数。

15.2.3   RandomList

作为容器的另一个例子,假设我们需要一个持有特定类型对象的列表,每次调用其上的select()方法时,它可以随机地选取一个元素。如果我们希望以此构建一个可以应用于各种类型的对象的工具,就需要使用泛型:



 



 

练习6:(1) 使用RandomList来处理两种额外的不同类型的元素,要区别于main()中已经用过的类型。

<!---->
  • 大小: 6.9 KB
  • 大小: 19.7 KB
  • 大小: 18.1 KB
  • 大小: 7.6 KB
  • 大小: 3.3 KB
  • 大小: 39 KB
  • 大小: 43.5 KB
  • 大小: 31.4 KB
  • 大小: 4.1 KB
  • 大小: 23.5 KB
分享到:
评论

相关推荐

    Java 编程思想第4版

    《Java编程思想第4版》是一本深受程序员喜爱的经典教程,尤其对于初学者而言,它提供了全面而深入的Java学习路径。这本书由Bruce Eckel撰写,被誉为“Thinking in Java”,其核心理念是引导读者深入理解Java语言的...

    java编程思想第四版源代码.7z

    《Java编程思想》是 Bruce Eckel 的经典著作,第四版更是深入浅出地介绍了Java语言的核心概念和技术。这个压缩包包含的源代码是书中的示例程序,它们旨在帮助读者理解书中阐述的各种编程原理和实践。通过分析这些源...

    Java 编程思想 第4版(英文版 pdf)

    《Java编程思想》第四版是Java开发者不可或缺的经典之作,由Bruce Eckel撰写,全面而深入地探讨了Java语言的核心概念和技术。这本书对于理解和掌握Java编程语言具有极高的价值,无论你是初学者还是经验丰富的程序员...

    (Thinking in Java)编程思想第4版习题答案

    《编程思想第4版》是Java编程领域的一本经典著作,由Bruce Eckel撰写,它深入浅出地讲解了面向对象编程的概念和技术。本压缩包包含该书的习题答案,分为TXT和PDF两种格式,方便读者根据需求选择查看方式。 在学习...

    Java编程思想_第4版—习题答案.rar

    而"Java编程思想_第4版—习题答案"这个压缩包则为读者提供了书中的习题解答,是学习过程中不可或缺的辅助资料。 1. 面向对象编程:Java是一种面向对象的语言,它强调将数据和操作数据的方法封装在一起,形成对象。...

    Java编程思想【第4版】习题答案

    在"Java编程思想【第4版】习题答案"这个压缩包中,你将找到作者或读者提供的对书中原有习题的解答。这些答案涵盖了从基础语法到高级特性的大量知识点,包括但不限于: 1. **基础语法**:Java的基础语法如变量声明、...

    java编程think第4版完整源码加python测试框架

    java编程think第4版完整源码加python测试框架,不难看出这是一本经典之作。本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、JavaI/O...

    Java 编程思想.第四版.课后练习答案

    《Java编程思想》是 Bruce Eckel 的经典著作,第四版更是深受广大Java程序员喜爱的一本教程。这本书深入浅出地讲解了Java语言的核心概念和技术,包括面向对象编程、泛型、集合框架、IO流、多线程、网络编程等多个...

    java 思想编程第4版

    这本《Java思想编程第4版》不仅涵盖了以上内容,还包括了更多的高级主题,如设计模式、并发编程、JVM优化等,是每个Java开发者必备的参考书籍之一。通过阅读和实践书中的例子,读者可以逐步提升自己的编程技能,深入...

    java编程第4版完整版,完整书签,扫描但文字可搜索复制

    《Java编程思想》第四版是一本经典的Java编程书籍,由Bruce Eckel编写,全面而深入地介绍了Java语言。该书被广泛认可为学习Java语言的重要资源,尤其适合有一定编程基础,希望深入理解Java及其编程范式的读者。 ## ...

    Java编程思想第十五章泛型(4).pptx

    Java编程思想第十五章泛型(4).pptx

    《java编程思想》_java编程思想_java编程思想_ThinkinginJava_mileefx_

    《Java编程思想》是Java程序员领域的一本经典之作,由Bruce Eckel撰写,以其深入浅出的讲解方式和丰富的实例闻名。这本书对于想要深入理解Java语言的人来说,是一份宝贵的资源。"Thinking in Java",直译为“思考...

    Java编程思想_第4版_源代码

    《Java编程思想》是Bruce Eckel的经典之作,其第四版更是深受全球程序员喜爱。这本书深入浅出地介绍了Java语言的核心概念和技术,对于初学者和经验丰富的开发者来说都是极好的参考资料。书中通过大量的实例来讲解...

    JAVA编程思想中文版.zip

    《JAVA编程思想》是 Bruce Eckel 的经典著作,中文版为国内Java开发者提供了深入理解Java语言的宝贵资源。这本书全面而深入地介绍了Java编程的核心概念和技术,是学习和提升Java编程技能的重要参考资料。 本书主要...

    Thinking in Java专用jar包-第4版(Java编程思想专用jar包)

    《Thinking in Java》是Bruce Eckel的经典之作,它深入浅出地介绍了Java编程语言的核心概念和技术。这个专用的jar包是配合第四版书籍使用的,包含了书中提到的一些实用工具类和示例代码,以便读者在实践过程中能更好...

    thinkinjava4源码-Thinking-In-Java4e:java编程思想第4版

    源码包"Thinking-In-Java4e:java编程思想第4版"包含了与书中讲解内容对应的示例代码,是学习和理解Java编程思想的重要资源。 1. **面向对象编程基础**:Java是一种面向对象的编程语言,书中通过实例详细解释了类、...

    java 编程思想第四版习题答案

    《Java编程思想第四版》是Java编程领域里一本经典的教程,由 Bruce Eckel 所著。这本书深入浅出地介绍了Java语言的核心概念和技术,包括面向对象编程、异常处理、集合框架、多线程、网络编程等多个方面。对于学习...

    Java编程思想源代码及课后练习代码

    《Java编程思想》是一本由 Bruce Eckel 编著的经典Java教程,对于初学者和有经验的程序员来说,都是深入理解Java语言的重要参考书。这本书深入浅出地讲解了Java的核心概念,包括面向对象编程、泛型、并发、集合框架...

Global site tag (gtag.js) - Google Analytics