泛型是“参数化类型”,也有形参和实参,其形参一般是用<T>/<E>/<K>/<V>等方式表示,实参则是在调用时传入它的引用类型,如String、Double等,注意是引用类型,不是基本类型;使用泛型的好处是能在编译阶段就防止一些错误的发生。
【1】泛型定义
【2】泛型类
【3】泛型方法
【4】泛型接口
【5】类型擦除
【6】类型通配符
【7】总结
泛型定义
什么是“泛型”,其实就是“宽泛的数据类型”,就是什么类型都可以的啦
泛型类
从下面的泛型类的例子中可以看到,类的定义时后面多了<T1, T2>,这是他的类型参数,当使用时被替换成了<Integer, Interger>等,这个叫做传值参数,T表示一般意义上的数据类型,K表示键值,V表示值,E表示错误,泛型的使用方式就是Person<String, Double> p2 = new Person<String, Double>();这样的。
package Gener; public class GenerClass { public static void main(String args[]){ Person<Integer, Integer> p1 = new Person<Integer, Integer>(); p1.setName(88); p1.setAge(99); System.out.println("姓名:"+p1.getName()+" 年龄:"+p1.getAge()); Person<String, Double> p2 = new Person<String, Double>(); p2.setName("zhangsan"); p2.setAge(77.99D); System.out.println("姓名:"+p2.getName()+" 年龄:"+p2.getAge()); } } class Person<T1, T2>{ T1 name; T2 age; public void setName(T1 name){ this.name = name; } public void setAge(T2 age){ this.age = age; } public T1 getName(){ return this.name; } public T2 getAge(){ return this.age; } }
泛型方法的使用和普通的方法差不多,只不过在定义的时候多了一个类型参数<T1, T2>,类型参数需要放在修饰符的后边、返回值的前边。使用泛型方法时不必指明参数的类型,直接使用即可。为了说明泛型类和泛型方法之间没有必然的联系,下面的例子中没有使用泛型类,直接调用的泛型方法。
package Gener; public class GenerMethod { public static void main(String args[]){ PersonMethod p1 = new PersonMethod(); p1.setName("lisi"); p1.setAge(99); p1.printPerson(p1.getName(), p1.getAge()); PersonMethod p2 = new PersonMethod(); p2.setName("zhangsan"); p2.setAge(77); p2.printPerson(p2.getName(), p2.getAge()); } } class PersonMethod{ String name; int age; public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age; } public String getName(){ return this.name; } public int getAge(){ return this.age; } public <T1, T2> void printPerson(T1 x, T2 y){ T1 m = x; T2 n = y; System.out.println("姓名:" + m + "年龄:" + n); } }
泛型接口和泛型方法是类似的,也是要在接口名字定义的后面加上<T>等类型参数,另外继承他的抽象类也要加上相同的类型参数<T>,还需要注意抽象接口的定义和使用方法。
package Gener; public class GenerInterface { public static void main(String args[]){ Human<String> h = new PersonInterface<String>(); h.setName("zhangsan"); System.out.println("NAME IS:"+ h.getName()); } } interface Human<T>{ public void setName(T name); public T getName(); } class PersonInterface<T> implements Human<T>{ private T name; public void setName(T name){ this.name = name; } public T getName(){ return this.name; } }
如果在使用泛型时没有指定数据类型,那么就会擦除泛型类型。但是类型的擦除是有一定风险的,应为在打印输出的时候需要向下转型,所以很容易发生错误。请看下面的例子:
package Gener; public class GenerEras { public static void main(String args[]){ PersonEras p1 = new PersonEras();// 类型的擦除 p1.setName(88); p1.setAge(99); System.out.println("姓名:"+(Integer)p1.getName()+" 年龄:"+(Integer)p1.getAge()); // 上面的这句强制类型转换是,泛型的向下转型 PersonEras p2 = new PersonEras();// 类型的擦除 p2.setName("zhangsan"); p2.setAge(77.99D); System.out.println("姓名:"+(String)p2.getName()+" 年龄:"+(Double)p2.getAge()); } } class PersonEras<T1, T2>{ T1 name; T2 age; public void setName(T1 name){ this.name = name; } public void setAge(T2 age){ this.age = age; } public T1 getName(){ return this.name; } public T2 getAge(){ return this.age; } }
下边例子中的PersonChar<?> 是PersonChar<String>/PersonChar<Integer>/PersonChar<Number>逻辑父类,所以我们可以定义这样的泛型方法来最终简化代码。
此外、我们还听说过类型通配符上限、类型通配符下限这样的说法,上限的表示形式为Person<? extends Number>、下限表示形式为Person<? super Number>、这两个正好相反,上限代表只能是Number类型及其子类,下限代表只能接收这个类以及其父类。
package Gener; public class GenerChar { public static void main(String args[]){ PersonChar<String> name1 = new PersonChar(); PersonChar<Integer> name2 = new PersonChar(); PersonChar<Number> name3 = new PersonChar(); name1.setName("zhangsan"); name2.setName(88); name3.setName(99.89D); getData(name1); getData(name2); getData(name3); } public static void getData(PersonChar<?> per){ System.out.println("姓名:"+per.getName()); } // } class PersonChar<T>{ private T name; public void setName(T name){ this.name = name; } public T getName(){ return this.name; } }总结
使用泛型的思想就是在开发中可以简化开发,如果因为使用了泛型而使开发更复杂,那么不如不使用,或者说明你使用的不够熟练,看了好多博客再加上自己的理解总结了这一点,本笔记还有待补充,未完待续。。。