步步理解 JAVA 泛型编程(二)

前面讲了泛型类的定义,你也可以在一个普通类中单单定义一个泛型方法。也就是说类能够带个类型参数,方法也可以带类型参数的。还是来看个例子(包括如何应用),一个获得数组中间元素的方法,因为数组中存储元素的类型是不定的,所以把该方法定义成泛型的。

我们之所以说上面的 ArrayAlg 是个普通类,是因为没有在类声明部分引入类型参数(比如声明为 public class ArrayAlg<T>)。同时在理解上面的泛型方法 getMiddel() 时应联想到泛型类是如何定义的。

对比前面泛型类的定义 public class Demo<T>{.......},那么在类的变量、方法参数、返回值等处就可以使用参数类型 T。
这里定义了泛型方法 public static <T> T getMiddle(T[] a){......},同样是用 <T> 的形式为方法引入了一个类型参数,那么这个类型 T 可用作该方法的返回值、参数、或局部变量。注意这里的 <T> T,前部分 <T> 是定义泛型方法的类型参数,后部 T 是该方法的返回值。

泛型类的类型参数(<T>) 是紧贴着类名的后面,而泛型方法的类型参数(<T>) 是紧贴着方法声明的返回类型的前面。

我们在使用泛型类,也是在构造的时候类紧贴类名后加上具体的参数类型,如 Demo<String> demo = new Demo<String>();类似的,我们在使用泛型方法时,从代码语法是在紧贴方法名的前面加代换上具体的参数类型,如ArrayAlg.<String>getMiddle(names),调用方法时不能有返回类型了,所以具体参数类型 <String> 靠紧了方法名。

前面代码中,我们说既可以用 ArrayAlg.<String>getMiddle(names);  来调用定义的泛型方法 public static <T> T getMiddle(T[] a),也可省写为
ArrayAlg.getMiddle(names);  来调用该方法。通常我们是这么做的,原因是 Java 编译器通过参数类型、个数等信息能推断出调用哪一个方法。但 Java 编译器也不是完全可靠的,有时候你必须显式的用 ArrayAlg.<String>getMiddle(names);  这种形式去调用明确的方法。

例如,我们在 ArrayAlg 中多定义一个 public static String getMiddle(String[] a){......} 方法,完整代码如下:

这也有些像我们的 C++ 的模板类,在模板具体化的时候存在 隐式实例化、显式实例化、显式具体化、部分具体化的情况,怎么看 C++ 的模板类还是要比 Java 的泛型复杂。

当然,上面代码只是说明 Java 的泛型方法在语法上会出现这种情况,倘若谁真写出的泛型代码需要用 ArrayAlg.<String>getMiddle(names);  显式的去调用泛型方法,那一定要考虑重构它了。明白了这一点难道就没有半点实际的意义吗,自然也不是,我们可以把它牢记为潜在的 Bug 容身之所。

进一步联系到前一篇,泛型类在定义的时候可以指定多个类型参数(用 <T,U> 形式),在定义泛型方法时同样用 <T,U> 的形式,调用的时候与一个参数时类似,如 ArrayAlg.<String, Date>getByIdx(names, new Date())。也不怕浪费几个字,大致浏览一下多类型参数时泛型方法的定义与使用的代码:

因为现在还不想涉及到调用类型参数的特定方法,所以参照 HashMap 算法,由第二个类型参数算出数组范围内的索引。留意两种调用泛型方法的方式,应用隐式调用在有些情况下也是会产生二义性的。

本文链接 https://yanbin.blog/understand-java-generic-2/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

9 Comments
Inline Feedbacks
View all comments
SEA
SEA
13 years ago

写得非常好,学习了,但是我在跟着做的时候,第一段代码的21行在eclipse编译之前就没办法通过,有如下提示“The method getMiddle(String[]) of type ArrayAlg is not generic; it cannot be

parameterized with arguments <String>”

也就是当定义两个相同方法时,一个泛型方法,一个普通的同名同返回参数时,就会出错的,不能同时存在?

SEA
SEA
13 years ago

@SEA

补充:在main中使用 String middle = ArrayAlg .<String>getMiddle(names)时,getMiddle下面出现红线的eclipse错误提示The method getMiddle(String[]) of type ArrayAlg is not generic; it cannot be

parameterized with arguments <String>”

隔叶黄莺
13 years ago

@SEA

请问一下你的 JDK 和 Eclipse 版本分别是什么?

我的 JDK 是 1.6.0_18,Eclipse 是3.5.2,编译第二段代码不会有什么问题的。

俏物悄语
13 years ago

写得非常好,学习了

SEA
SEA
13 years ago

@隔叶黄莺

jdk是1.6.0_17-b04

eclipse是 3.3.2

隔叶黄莺
13 years ago

@SEA

差不多的版本,看一下你旁边的人会不会是这种情况,如果他们可以的话,对照了,找到问题了也别忘记拿来分享一下哦!

SEA
SEA
13 years ago

@隔叶黄莺

原来是eclipse版本问题,我现在换成eclipse 3.5.2就没有可以了,是不是3.3.2还不完全支持泛型?

税国政
税国政
13 years ago

谢谢楼主分享!

oolala
13 years ago

写的很好,很强大。看了好几篇文章,这个写的是最容易理解的。

功夫不负有心人,终于搜索到了一能理解的文章。