`

Java编程思想(第4版) 之 15.4 泛型方法

阅读更多

15.4   泛型方法

到目前为止,我们看到的泛型,都是应用于整个类上。但同样可以在类中包含参数化方法,而这个方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是否是泛型没有关系。

泛型方法使得该方法能够独立于类而产生变化。以下是一个基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更清楚明白。另外,对于一个static的方法而言,无法访问泛型类的类型参数,所以,如果static方法需要使用泛型能力,就必须使其成为泛型方法。

要定义泛型方法,只需将泛型参数列表置于返回值之前,就像下面这样:



 

GenericMethods并不是参数化的,尽管这个类和其内部的方法可以被同时参数化,但是在这个例子中,只有方法f()拥有类型参数。这是由该方法的返回类型前面的类型参数列表指明的。

注意,当使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断(type argument inference)。因此,我们可以像调用普通方法一样调用f(),而且就好像是f()被无限次地重载过。它甚至可以接受GenericMethods作为其类型参数。

如果调用f()时传入基本类型,自动打包机制就会介入其中,将基本类型的值包装为对应的对象。事实上,泛型方法与自动打包避免了许多以前我们不得不自己编写出来的代码。

练习9:(1) 修改GenericMethods.java类,使f()可以接受三个类型各不相同的参数。

练习10:(1) 修改前一个练习,将方法f()的其中一个参数修改为非参数化的类型。

15.4.1   杠杆利用类型参数推断

人们对泛型有一个抱怨,使用泛型有时候需要向程序中加入更多的代码。考虑第11章中的holding/MapOfList.java类,如果要创建一个持有List的Map,就要像下面这样:



 

(本章稍后会介绍表达式中问号与extends的用法。)看到了吧,你在重复自己做过的事情,编译器本来应该能够从泛型参数列表中的一个参数推断出另一个参数。唉,可惜的是,编译器暂时还做不到。然而,在泛型方法中,类型参数推断可以为我们简化一部分工作。例如,我们可以编写一个工具类,它包含各种各样的static方法,专门用来创建各种常用的容器对象:



 

main()方法演示了如何使用这个工具类,类型参数推断避免了重复的泛型参数列表。它同样可以应用于holding/MapOfList.java:



 

对于类型参数推断而言,这是一个有趣的例子。不过,很难说它为我们带来了多少好处。如果某人阅读以上代码,他必须分析理解工具类New,以及New所隐含的功能。而这似乎与不使用New时(具有重复的类型参数列表的定义)的工作效率差不多。这真够讽刺的,要知道,我们引入New工具类的目的,正是为了使代码简单易读。不过,如果标准Java类库要是能添加类似New.java这样的工具类的话,我们还是应该使用这样的工具类。

类型推断只对赋值操作有效,其他时候并不起作用。如果你将一个泛型方法调用的结果(例如New.map())作为参数,传递给另一个方法,这时编译器并不会执行类型推断。在这种情况下,编译器认为:调用泛型方法后,其返回值被赋给一个Object类型的变量。下面的例子证明了这一点:



 

练习11:(1) 创建自己的若干个类来测试New.java,并确保New可以正确地与它们一起工作。

显式的类型说明

在泛型方法中,可以显式地指明类型,不过这种语法很少使用。要显式地指明类型,必须在点操作符与方法名之间插入尖括号,然后把类型置于尖括号内。如果是在定义该方法的类的内部,必须在点操作符之前使用this关键字,如果是使用static的方法,必须在点操作符之前加上类名。使用这种语法,可以解决LimitsOfInference.java中的问题:



 

当然,这种语法抵消了New类为我们带来的好处(即省去了大量的类型说明),不过,只有在编写非赋值语句时,我们才需要这样的额外说明。

练习12:(1) 使用显式的类型说明来重复前一个练习。

15.4.2   可变参数与泛型方法

泛型方法与可变参数列表能够很好地共存:



 



 

makeList()方法展示了与标准类库中java.util.Arrays.asList()方法相同的功能。

15.4.3   用于Generator的泛型方法

利用生成器,我们可以很方便地填充一个Collection,而泛型化这种操作是具有实际意义的:



 

请注意,fill()方法是如何透明地应用于Coffee和Integer的容器和生成器。

练习13:(4) 重载fill()方法,使其参数与返回值的类型为Collection的导出类:List、Queue和Set。通过这种方式,我们就不会丢失容器的类型。能够在重载时区分List和LinkedList吗?

15.4.4   一个通用的Generator

下面的程序可以为任何类构造一个Generator,只要该类具有默认的构造器。为了减少类型声明,它提供了一个泛型方法,用以生成BasicGenerator:



 



 

这个类提供了一个基本实现,用以生成某个类的对象。这个类必需具备两个特点:(1)它必须声明为public。(因为BasicGenerator与要处理的类在不同的包中,所以该类必须声明为public,并且不只具有包内访问权限。)(2)它必须具备默认的构造器(无参数的构造器)。要创建这样的BasicGenerator对象,只需调用create()方法,并传入想要生成的类型。泛型化的create()方法允许执行BasicGenerator.create(MyType.class),而不必执行麻烦的new Basic-Generator<MyType>(MyType.class)。

例如,下面是一个具有默认构造器的简单的类:



 

CountedObject类能够记录下它创建了多少个CountedObject实例,并通过toString()方法告诉我们其编号。

使用BasicGenerator,你可以很容易地为CountedObject创建一个Generator:



 

可以看到,使用泛型方法创建Generator对象,大大减少了我们要编写的代码。Java泛型要求传入Class对象,以便也可以在create()方法中用它进行类型推断。

练习14:(1) 修改BasicGeneratorDemo.java类,使其显式地构造Generator(也就是不使用create()方法,而是使用显式的构造器)。

15.4.5   简化元组的使用

有了类型参数推断,再加上static方法,我们可以重新编写之前看到的元组工具,使其成为更通用的工具类库。在这个类中,我们通过重载static方法创建元组:



 

下面是修改后的TupleTest.java,用来测试Tuple.java:



 

注意,方法f()返回一个参数化的TwoTuple对象,而f2()返回的是非参数化的TwoTuple对象。在这个例子中,编译器并没有关于f2()的警告信息,因为我们并没有将其返回值作为参数化对象使用。在某种意义上,它被“向上转型”为一个非参数化的TwoTuple。然而,如果试图将f2()的返回值转型为参数化的TwoTuple,编译器就会发出警告。

练习15:(1) 验证前面的陈述是否属实。

练习16:(2) 为Tuple.java添加一个SixTuple,并在TupleTest2.java中进行测试。

15.4.6   一个Set实用工具

作为泛型方法的另一个示例,我们看看如何用Set来表达数学中的关系式。通过使用泛型方法,可以很方便地做到这一点,而且可以应用于多种类型:



 

在前三个方法中,都将第一个参数Set复制了一份,将Set中的所有引用都存入一个新的HashSet对象中,因此,我们并未直接修改参数中的Set。返回的值是一个全新的Set对象。

这四个方法表达了如下的数学集合操作:union()返回一个Set,它将两个参数合并在一起;intersection()返回的Set只包含两个参数共有的部分;difference()方法从superset中移除subset包含的元素;complement()返回的Set包含除了交集之外的所有元素。下面提供了一个enum,它包含各种水彩画的颜色。我们将用它来演示以上这些方法的功能和效果。



 

为了方便起见(可以直接使用enum中的元素名),下面的示例以static的方式引入Watercolors。这个示例使用了EnumSet,这是Java SE5中的新工具,用来从enum直接创建Set。(在第19章中,我们会详细介绍EnumSet。)在这里,我们向static方法EnumSet.range()传入某个范围的第一个元素与最后一个元素,然后它将返回一个Set,其中包含该范围内的所有元素:



 

我们可以从输出中看到各种关系运算的结果。

下面的示例使用Sets.difference()打印出java.util包中各种Collection类与Map类之间的方法差异:



 



 

在第11章的“总结”中,我们使用了这个程序的输出结果。

练习17:(4) 研究JDK文档中有关EnumSet的部分,你会看到它定义了clone()方法。然而,在Sets.java中,你却不能复制Set接口中的引用。请试着修改Sets.java,使其不但能接受一般的Set接口,而且能直接接受EnumSet,并使用clone()而不是创建新的HashSet对象。

<!---->
  • 大小: 20.2 KB
  • 大小: 3.4 KB
  • 大小: 30.5 KB
  • 大小: 12.9 KB
  • 大小: 11.8 KB
  • 大小: 11.5 KB
  • 大小: 10.7 KB
  • 大小: 12.2 KB
  • 大小: 26.3 KB
  • 大小: 10.2 KB
  • 大小: 16.1 KB
  • 大小: 10.9 KB
  • 大小: 16 KB
  • 大小: 24.4 KB
  • 大小: 45 KB
  • 大小: 27.5 KB
  • 大小: 18.6 KB
  • 大小: 74.2 KB
  • 大小: 12.5 KB
  • 大小: 49.3 KB
分享到:
评论

相关推荐

    Java 编程思想第4版

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

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

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

    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 带两个类型参数的泛型

    在Java编程语言中,泛型是一种强大的特性,它允许我们在类、接口和方法中使用类型参数,从而提高代码的灵活性和可复用性。当我们谈论“java带两个类型参数的泛型”时,这意味着我们正在处理一个泛型类或泛型方法,...

    java 泛型方法使用示例

    例如,一个接受两个相同类型参数并返回它们之和的泛型方法可以这样定义: ```java public &lt;T&gt; T sum(T a, T b) { return a + b; } ``` 在这个例子中,`&lt;T&gt;` 是类型参数,代表某种未知的数据类型,`T` 在方法体内...

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

    《Java编程思想》第四版详细阐述了泛型的概念、泛型类、泛型方法以及通配符的使用,使得读者能够编写更加安全、灵活的代码。 #### 集合框架 Java的集合框架是用于存储和操作数据的高效工具。本书深入讲解了集合...

    Java编程详细教程Java集合与泛型PPT教案学习.pptx

    Java编程详细教程Java集合与泛型PPT教案学习.pptx

    Java编程基础教程:枚举与泛型的深入讲解

    内容概要:本文深入介绍了Java中的两个重要特性——枚举和泛型。首先详细讲述了枚举的基本概念、枚举类型的特点及其实现方式,如枚举的成员方法、构造器以及如何将其用于高级编程场合。其次对泛型的概念进行了解释,...

    Java语言 泛型讲解案例代码 (泛型类、泛型接口、泛型方法、无界及上下限通配符、泛型对协变和逆变的支持、类型擦除 ...)

    泛型方法:演示如何在普通类中定义泛型方法,以及如何调用和使用泛型方法。 类型通配符:展示如何使用类型通配符来增加灵活性,以及如何进行类型边界约束。 泛型接口:演示如何定义和实现泛型接口,并通过示例代码...

    Java中的泛型方法演示代码

    在这个“Java中的泛型方法演示代码”中,我们可以期待看到如何在Java中定义和使用泛型方法的实例。 首先,泛型方法的基本语法是在方法声明前加上尖括号`&lt;&gt;`,并在其中定义一个或多个类型参数。例如: ```java ...

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

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

Global site tag (gtag.js) - Google Analytics