本篇内容源于本人一个好友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));
七、多元泛型(待续)