JAVA泛型那些事儿

本篇内容源于本人一个好友sgpro提供的java学习例子,现拿出来给大家分享。

此例子非常直观的通过代码讲解了java泛型的用法和好处,是笔者一直珍藏的最好的泛型学习笔记。

一、面向过程的时代

我们先从求最大值开始讲起。

在面向过程的编程世界里,我们是这样求最大值的

 1 package _00_OP;
 2 /**
 3  * 求若干个数的较大的一个
 4  *
 5  * 不使用重载,面向过程的年代。
 6  *
 7  * @author sgpro
 8  *
 9  */
10 class MaxNumber {
11
12     /*求两个int类型数据的最大值*/
13     public static int max2ForInteger(int a, int b) {
14         return a > b? a : b;
15     }
16
17     /*求两个char类型数据的最大值*/
18     public static char max2ForChar(char a, char b) {
19         return a > b? a : b;
20     }
21
22     /*求三个char类型数据的最大值,通过两次调用max2ForChar来实现*/
23     public static int max3ForIneger(int a, int b, int c) {
24         return max2ForInteger(a, max2ForInteger(b, c));
25     }
26 }

从上面的代码可以看到,在面向过程的世界里,如果要想取最大值要考虑

1.数据类型,多少种类型就要多少个方法

2.数据数量,数量越多,算法就越复杂,例子中是使用递归的方法实现3个数据求max的。

总之,很麻烦

二、重载Overload的使用

方法重载:

通过参数个数不同或者参数类型不同实现相同的方法名得到不同的结果

还是求取最大值的例子

 1 package _01_Overload;
 2
 3 /**
 4  * 求若干个数的较大的一个
 5  *
 6  * 方法重载
 7  * 通过参数个数不同或者参数类型不同实现相同的方法名得到不同的结果
 8  *
 9  * @author sgpro
10  *
11  */
12
13 class MaxNumber {
14
15     /*求两个int类型数据的最大值*/
16     public static int max(int a, int b) {
17         return a > b? a : b;
18     }
19
20     /*求两个char类型数据的最大值*/
21     public static char max(char a, char b) {
22         return a > b? a : b;
23     }
24
25     /*求三个int类型数据的最大值,通过两次调用max来实现*/
26     public static int max(int a, int b, int c) {
27         return max(a, max(b, c));
28     }
29 }

我们可以看到,相对面向过程的代码,我们实现了方法名的统一。

当然,代码依然很复杂,不方便。

三、模板(Template)的使用

关于方法模板几个注意

1. java的模板类的模板参数只能是参数类型,成员变量类型等,模板名是确定的。

2. 运行期,模板参数会被当作Object来处理

3. 使用模板类的类型安全,只是利用编译器的类型检查,来自动保证运行期的类型强转的正确与安全。

 1 package _02_Template;
 2
 3 /**
 4  * 求若干个数的较大的一个
 5  *
 6  * 通过模板来实现,java 不支持纯粹的  “方法模板”,此列事实上就是泛型的前奏了
 7  *
 8  * @author sgpro
 9  *
10  */
11
12 class MaxNumber {
13
14     public static <T extends Object> T max(T a, T b) {
15
16         T ret = null;
17         /*
18          * java不支持纯粹的方法模板,具体的比较处理中需要对类型进行判断。
19          */
20         if (a instanceof Integer) {
21             ret = (Integer)a > (Integer)b ? a : b;
22         } else if (a instanceof Character) {
23             ret =  (Character)a > (Character)b ? a : b;
24         }
25
26         return ret;
27     }
28
29
30     public static <T extends Object> T max(T a, T b, T c) {
31         return max(a, max(b, c));
32     }
33
34
35
36 }

由于java不支持纯粹的方法模板,所以具体的比较器处理中还需要对类型进行判断。

四.还是maxNumber,现在我们使用泛型来实现。

首先我们知道,int类型可以自动装箱为Integer,char类型可以自动装箱为Charactor,而Integer类型和Charactor类型都已经实现了Comparable接口

因此,我们可以采用泛型来实现实现maxNumber

 1 package _03_Generic;
 2
 3 /**
 4  * 求若干个数的较大的一个
 5  *
 6  * 泛型实例 一,静态方式, 无差异提供者。
 7  *
 8  * @author sgpro
 9  *
10  */
11
12 class MaxNumber<T extends Comparable<T>> {  //T的上限是Comparable<T>  T必须实现了Comparable<T>接口
13
14     public T  max(T a, T b) {
15         if (a != null && b != null) {
16             return a.compareTo(b) > 0? a : b;
17         } else {
18             throw new IllegalArgumentException("参数有误,空对象不能比较");
19         }
20     }
21
22     public T max(T a, T b, T c) {
23         return max(a, max(b, c));
24     }
25
26 }

java的泛型是伪泛型,使用前我们需要确定具体的类型

		MaxNumber<Character> forChar = new MaxNumber<Character>() ;
		MaxNumber<Integer> forInteger = new MaxNumber<Integer>() ;
		System.out.println(forChar.max(‘x‘, ‘z‘));
		System.out.println(forInteger.max(100, 200));
		System.out.println(forInteger.max(200, 800, 400));

  

我们看到上面的maxNumber类的参数T要求实现comparable接口。那么只要是实现了comparable接口的对象都可以使用maxNumber来求最大值。

comparable:顾名思义,就是可以比较的。实现了这个接口,就可以使用><来进行对象间的比较。

举个例子:我们实现一个Country类,实现了Comparable接口的compareTo了方法,两个Country对象比大小,population大的大

 1 package _03_Generic;
 2
 3 public class Country implements Comparable<Country> {
 4
 5     public long getPopulation() {
 6         return population;
 7     }
 8
 9     public void setPopulation(long population) {
10         this.population = population;
11     }
12
13     public long getGdp() {
14         return gdp;
15     }
16
17     public void setGdp(long gdp) {
18         this.gdp = gdp;
19     }
20
21     public long getTz() {
22         return tz;
23     }
24
25     public void setTz(long tz) {
26         this.tz = tz;
27     }
28
29     private long population;
30     private long gdp;
31     private long tz;
32     private String name;
33
34     public Country(String name, long population, long gdp, long tz) {
35         super();
36         this.population = population;
37         this.gdp = gdp;
38         this.tz = tz;
39         this.name = name;
40     }
41
42     public String getName() {
43         return name;
44     }
45
46     public void setName(String name) {
47         this.name = name;
48     }
49
50     public Country() {
51         // TODO Auto-generated constructor stub
52     }
53
54
55     @Override
56     public int compareTo(Country o) {
57         // TODO Auto-generated method stub
58         return  population > o.population ? 1 : -1;
59     }
60
61     @Override
62     public String toString() {
63         // TODO Auto-generated method stub
64         return name;
65     }
66
67 }

然后我们还是使用maxNumber类来取最大值:“中国,俄罗斯,美国三个国家比较,比的是人口”

1          Country china =  new Country("中国",   1300000000L, 3000, 8);
2          Country russia =  new Country("俄罗斯", 100000000L, 8000, 1);
3          Country america =  new Country("美国",  400000000L, 32000, -5);
4
5          MaxNumber<Country> forCountry = new MaxNumber<Country>();
6
7          System.out.println(forCountry.max(russia, america));
8          System.out.println(forCountry.max(china, russia, america));

五、延续上面的Country例子,我们知道Country类比较大小,比的是人口。那如果我希望在不改变Country类代码的情况下让Country按照GDP比大小,我们应该怎么做呢?

我们可以在maxNumber泛型类中加入一个比较器——Comparator

 1 package _04_Generic;
 2
 3 import java.util.Comparator;
 4
 5 /**
 6  * 求若干个数的较大的一个
 7  *
 8  * 泛型实例 二,动态方式,开放差异提供者,支持默认比较器,释放比较器
 9  *
10  * @author sgpro
11  *
12  */
13
14
15 class MaxNumber<T extends Comparable<T>> {
16
17     public MaxNumber(Comparator<T> comp) {
18         super();
19         this.comp = comp;
20     }
21
22     private Comparator<T> comp = null;
23
24     public MaxNumber() {
25     }
26
27
28     public Comparator<T> getComp() {
29         return comp;
30     }
31
32     public void setComp(Comparator<T> comp) {
33         this.comp = comp;
34     }
35
36     public T  max(T a, T b) {
37         if (a != null && b != null) {
38             if (comp == null) {
39                 return  a.compareTo(b) > 0? a : b;
40             } else {
41                 return  comp.compare(a, b)  > 0? a : b;
42             }
43         } else {
44             throw new IllegalArgumentException("参数有误,空对象不能比较");
45         }
46     }
47
48     public T max(T a, T b, T c) {
49         return max(a, max(b, c));
50     }
51
52 }

然后在实例化maxNumber之后,setComp设置比较器comparator

 1          Country china =  new Country("中国",   1300000000L, 3000, 8);
 2          Country russia =  new Country("俄罗斯", 100000000L, 8000, 1);
 3          Country america =  new Country("美国",  400000000L, 32000, -5);
 4
 5          System.out.println(forCountry.max(russia, america));
 6          System.out.println(forCountry.max(china, russia, america));
 7
 8          forCountry.setComp(new Comparator<Country>() {
 9
10             @Override
11             public int compare(Country o1, Country o2) {
12                 // TODO Auto-generated method stub
13                 return o1.getGdp() > o2.getGdp()? 1 : -1;
14             }
15         });
16
17          System.out.println(forCountry.max(russia, america));
18          System.out.println(forCountry.max(china, russia, america));

六、以上我们最多也就是比较三个对象的大小。那么如果我们需要比较三个以上对象的大小呢?

我们需要再对maxNumber做好排序,

 1 package _05_Generic;
 2
 3 import java.util.Comparator;
 4
 5 /**
 6  * 求若干个数的较大的一个
 7  *
 8  * 泛型实例 三,进一步抽象算法,可以比较N个,比较器动态化,支持默认比较器
 9  *
10  * @author sgpro
11  *
12  */
13
14 class MaxNumber<T extends Comparable<T>>{
15
16     public MaxNumber(Comparator<T> comp) {
17         super();
18         this.comp = comp;
19     }
20
21     private Comparator<T> comp = null;
22
23     public MaxNumber() {
24     }
25
26     public Comparator<T> getComp() {
27         return comp;
28     }
29
30     public void setComp(Comparator<T> comp) {
31         this.comp = comp;
32     }
33
34     public T max(T... list) {
35         T ret = null;
36         if (list != null && list.length != 0) {
37             ret = list[0];
38             if (comp == null) {
39                 for (T o : list) {
40                     ret = o != null && o.compareTo(ret) > 0 ? o : ret;
41                 }
42             }
43             else {
44                 for (T o : list) {
45                     ret = o != null && comp.compare(o, ret) > 0 ? o : ret;
46                 }
47             }
48         }
49         else {
50             throw new IllegalArgumentException("参数有误,比较元素序列为空");
51         }
52         return ret;
53     }
54 }

然后还是一样,实例化maxNumber,设置comparator比较器进行比较。

下面还是使用Country的例子,分别按照默认(人口)、GDP、人均GDP,对中国、俄罗斯、日本、美国四个国家进行全方面的比较。

 1          Country china =  new Country("中国",   13, 13000, 8);
 2          Country russia =  new Country("俄罗斯", 2, 5000, 1);
 3          Country japan =  new Country("日本",    2, 30000, 9);
 4          Country america =  new Country("美国",  4, 100000, -5);
 5
 6
 7          /**
 8           * 默认人口
 9           */
10          System.out.println(s.max(china, russia, japan, america));
11
12          /**
13           * GDP
14           */
15          s.setComp(new Comparator<Country>() {
16
17             @Override
18             public int compare(Country o1, Country o2) {
19                 // TODO Auto-generated method stub
20                 return o1.getGdp() > o2.getGdp()? 1 : -1;
21             }
22         });
23
24          System.out.println(s.max(china, russia, japan, america));
25
26          /**
27           * 人均GDP
28           */
29          Comparator<Country> avgGDPComp = new Comparator<Country>() {
30
31             @Override
32             public int compare(Country o1, Country o2) {
33                 // TODO Auto-generated method stub
34                 return o1.getGdp() / o1.getPopulation() > o2.getGdp() / o2.getPopulation()? -1 : 1;
35             }
36         };
37
38          s.setComp(avgGDPComp);
39
40          System.out.println(s.max(china, russia, japan, america));

七、多元泛型(待续)

时间: 2024-10-17 21:01:53

JAVA泛型那些事儿的相关文章

Java泛型中的PECS原则

今天在写代码的时候使用到了这样一个方法签名: public void foo(Map<String, String> map); 在写这个参数的时候正好在想一些关于泛型的东西,于是: public void foo(Map<? extends String, ? extends String> map); 这两种写法有什么区别呢?记得以前和同学讨论过这个问题,但后来没有记下来,渐渐又淡忘了.今天又去翻了好多资料,总算找到一些可以参考的,赶紧记在这里方便以后温故知新啦.好了,言归正传

Java泛型的协变

在上篇<Java泛型的基本使用>这篇文章中遗留下面问题,即将子类型也能加入到父类型的泛型中.要实现这样的功能必须借助于协变. 实验准备 如今在上篇文章展示的Decorator类型的基础上,添加一些代码,如代码清单1所看到的. 代码清单1 /** * * 描 述:Exp2使用br/> * 作 者:jiaan.gja<br/> * 历 史: (版本号) 作者 时间 凝视 <br/> * @param itemList */ public void doDecorate

2017.4.5 Java 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 假定我们有这样一个需求:写一个排序方法,能够对整形数组.字符串数组甚至其他任何类型的数组进行排序,该如何实现? 答案是可以使用 Java 泛型. 使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序.然后,调用该泛型方法来对整型数组.浮点数数组.字符串数组等进行排

关于Java泛型的使用

在目前我遇到的java项目中,泛型应用的最多的就属集合了.当要从数据库取出多个对象或者说是多条记录时,往往都要使用集合,那么为什么这么使用,或者使用时有什么要注意的地方,请关注以下内容. 感谢Windstep. 原文链接:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 原文标题:Java总结篇系列:Java泛型 (我的第一篇水文,233)

java 泛型 窜讲

一.为什么使用泛型      复用性:泛型的本质就是参数化类型,因而使用编写的泛型代码可以被许多不同类型的对象所复用.      安全性:在对类型Object引用的参数操作时,往往需要进行显式的强制类型转换.这种强制类型转换需要在运行时才能被发现是否转换异常,通过引入泛型能将在运行时才能检查类型转换,提前到编译时期就能检查. 二.自定义泛型 java中自定义泛型分为三种:泛型类.泛型接口.泛型方法. 下面使用一个案例演示泛型类.泛型方法,泛型接口类似,所以不再演示. // 自定义泛型类publi

1月21日 - (转)Java 泛型

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M

Java泛型_上界extends_下界super

?Java泛型_上界extends_下界super ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T或是T的子类 <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型(T)的超类型(父类型),直至Object 当使用 Upper Bound 通配符时 如下代码, /**  * 代码中通配符<?> 是 <? extends Object> 的简写  *  * @param list

C++泛型 &amp;&amp; Java泛型实现机制

C++泛型 C++泛型跟虚函数的运行时多态机制不同,泛型支持的静态多态,当类型信息可得的时候,利用编译期多态能够获得最大的效率和灵活性.当具体的类型信息不可得,就必须诉诸运行期多态了,即虚函数支持的动态多态. 对于C++泛型,每个实际类型都已被指明的泛型都会有独立的编码产生,也就是说list<int>和list<string>生成的是不同的代码,编译程序会在此时确保类型安全性.由于知道对象确切的类型,所以编译器进行代码生成的时候就不用运用RTTI,这使得泛型效率跟手动编码一样高.

java 泛型详解(普通泛型、 通配符、 泛型接口,泛型数组,泛型方法,泛型嵌套)

JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型. 1.Java泛型  其实Java的泛型就是创建一个用类型作为参数的类.就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1.str2的值是可变的.而泛型也是一样的,这样写class Java_Generics<K,V>,这里边的K和V就象方法中的参数str1和st