对于java的泛型我一直属于一知半解的,平常真心用的不多。直到阅读《Effect Java》,看到很多平常不了解的用法,才下定决心,需要系统的学习,并且记录下来。
根据《Java编程思想》中的描述,泛型出现的动机:
有很多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类。复制代码
泛型的思想很早就存在,如C++中的模板(Templates)。模板的精神:参数化类型
JDK 1.5时增加了泛型,在很大的程度上方便在集合上的使用。
public static void main(String[] args) { List list = new ArrayList(); list.add(11); list.add("ssss"); for (int i = 0; i < list.size(); i++) { System.out.println((String)list.get(i)); } }复制代码
因为list类型是Object。所以int,String类型的数据都是可以放入的,也是都可以取出的。但是上述的代码,运行的时候就会抛出类型转化异常,这个相信大家都能明白。
public static void main(String[] args) { List<String> list = new ArrayList(); list.add("hahah"); list.add("ssss"); for (int i = 0; i < list.size(); i++) { System.out.println((String)list.get(i)); } }复制代码
在上述的实例中,我们只能添加String类型的数据,否则编译器会报错。
泛型的三种使用方式:泛型类,泛型方法,泛型接口
public class 类名 <泛型类型1,...> { }复制代码
public <泛型类型> 返回类型 方法名(泛型类型 变量名) { }复制代码
class Demo{ public <T> T fun(T t){ // 可以接收任意类型的数据 return t ; // 直接把参数返回 } }; public class GenericsDemo26{ public static void main(String args[]){ Demo d = new Demo() ; // 实例化Demo对象 String str = d.fun("汤姆") ; // 传递字符串 int i = d.fun(30) ; // 传递数字,自动装箱 System.out.println(str) ; // 输出内容 System.out.println(i) ; // 输出内容 } };复制代码
public interface 接口名<泛型类型> { }复制代码
/** * 泛型接口的定义格式: 修饰符 interface 接口名<数据类型> {} */public interface Inter<T> { public abstract void show(T t) ;} /** * 子类是泛型类 */public class InterImpl<E> implements Inter<E> { @Override public void show(E t) { System.out.println(t); }} Inter<String> inter = new InterImpl<String>() ;inter.show("hello") ;复制代码
//定义接口时指定了一个类型形参,该形参名为Epublic interface List<E> extends Collection<E> { //在该接口里,E可以作为类型使用 public E get(int index) {} public void add(E e) {} } //定义类时指定了一个类型形参,该形参名为Epublic class ArrayList<E> extends AbstractList<E> implements List<E> { //在该类里,E可以作为类型使用 public void set(E e) { ....................... }}复制代码
父类派生子类的时候不能在包含类型形参,需要传入具体的类型
public class A extends Container<K, V> {}
public class A extends Container<Integer, String> {}
public class A extends Container {}
public class Person { public <T> Person(T t) { System.out.println(t); } }复制代码
使用:
public static void main(String[] args) { new Person(22);// 隐式 new <String> Person("hello");//显示}复制代码
public class Person<E> { public <T> Person(T t) { System.out.println(t); }}复制代码 正确用法: public static void main(String[] args) { Person<String> person = new Person("sss");}复制代码 PS:编译器会提醒你怎么做的
2.7.1背景:
2.7.2 <? extends T> 上界通配符
它表示集合中的所有元素都是Animal类型或者其子类 List<? extends Animal>复制代码
//Cat是其子类 List<? extends Animal> list = new ArrayList<Cat>();复制代码
2.7.3 <? super T> 下界通配符
它表示集合中的所有元素都是Cat类型或者其父类 List <? super Cat> 复制代码
//Animal是其父类List<? super Cat> list = new ArrayList<Animal>();复制代码
2.7.4 <?> 无界通配符
编译器编译带类型说明的集合时会去掉类型信息
public class GenericTest { public static void main(String[] args) { new GenericTest().testType(); } public void testType(){ ArrayList<Integer> collection1 = new ArrayList<Integer>(); ArrayList<String> collection2= new ArrayList<String>(); System.out.println(collection1.getClass()==collection2.getClass()); //两者class类型一样,即字节码一致 System.out.println(collection2.getClass().getName()); //class均为java.util.ArrayList,并无实际类型参数信息 }}复制代码
truejava.util.ArrayList复制代码
public class GenericTest { public static void main(String[] args) throws Exception { getParamType(); } /*利用反射获取方法参数的实际参数类型*/ public static void getParamType() throws NoSuchMethodException{ Method method = GenericTest.class.getMethod("applyMap",Map.class); //获取方法的泛型参数的类型 Type[] types = method.getGenericParameterTypes(); System.out.println(types[0]); //参数化的类型 ParameterizedType pType = (ParameterizedType)types[0]; //原始类型 System.out.println(pType.getRawType()); //实际类型参数 System.out.println(pType.getActualTypeArguments()[0]); System.out.println(pType.getActualTypeArguments()[1]); } /*供测试参数类型的方法*/ public static void applyMap(Map<Integer,String> map){ }}复制代码
java.util.Map<java.lang.Integer, java.lang.String>interface java.util.Mapclass java.lang.Integerclass java.lang.String复制代码
public static void main(String[] args) throws Exception { //定义一个包含int的链表 ArrayList<Integer> al = new ArrayList<Integer>(); al.add(1); al.add(2); //获取链表的add方法,注意这里是Object.class,如果写int.class会抛出NoSuchMethodException异常 Method m = al.getClass().getMethod("add", Object.class); //调用反射中的add方法加入一个string类型的元素,因为add方法的实际参数是Object m.invoke(al, "hello"); System.out.println(al.get(2)); } 复制代码
public class User<K, V> { public void show(K k) { // 报错信息:'show(K)' clashes with 'show(V)'; both methods have same erasure } public void show(V t) { }}复制代码
由于泛型擦除,二者本质上都是Obejct类型。方法是一样的,所以编译器会报错。
换一个方式:
public class User<K, V> { public void show(String k) { } public void show(V t) { }}复制代码
使用结果:
可以正常的使用
编译器也不知道该创建那种类型的对象
public class User<K, V> { private K key = new K(); // 报错:Type parameter 'K' cannot be instantiated directly }复制代码
静态方法无法访问类上定义的泛型;如果静态方法操作的类型不确定,必须要将泛型定义在方法上。
如果静态方法要使用泛型的话,必须将静态方法定义成泛型方法。
public class User<T> { //错误 private static T t; //错误 public static T getT() { return t; } //正确 public static <K> void test(K k) { }}复制代码
public class User<T> { private T[] values; public User(T[] values) { //错误,不能实例化元素类型为类型参数的数组 this.values = new T[5]; //正确,可以将values 指向类型兼容的数组的引用 this.values = values; }}复制代码
5.5 对泛型异常的限制
泛型类不能扩展 Throwable,意味着不能创建泛型异常类。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有