java 泛型
发展过程
由父类转给子类时允许的,但是会抛出ClassCastException。这种异常是运行时异常,编译期不会检查,这就加大了检查的难度。为了解决这个问题,从jdk5开始引入了泛型。泛型可以把ClassCastException转换成编译时类型不兼容错误。
泛型符号是<>,里面可以使任意一种类(不能是int等基础类型,可以是Integer).
例如: Set<Object> set = new HashSet<Object>();//实例中的类型必须要和前面相同
泛型类,数组,接口,方法
泛型类
例:
public class Bag<T> |
这和c++中的模板十分类似。同样泛型参数可以有许多个,例如:public class A<str,inte,dou>{...}
注意 如果没有传入泛型实参的话,泛型变量可以使任何类型。例如:
Generic generic = new Generic("111111"); |
泛型接口
泛型接口和泛型类类似,但是它的实现类如果实现的是泛型接口,那么它的实现类也要是泛型。例如:
public class A<T> implements B<T>
如果public class A implements B<T>
会报错
但是如果实现的是一个确定类型的接口,那么前面就不是必须加
public class A implements B<Integer>
可以
数组
以前我们都是 int[], double[],现在我们把前面的类型变成泛型。例如T[]
。
这里要注意一点,不能使用泛型创建实例。例如T[] content = new T[10];
是错误的。
方法
在普通类和泛型类中都可以定义泛型方法。泛型方法只需要在方法头部定义泛型符号就可以了,例如:
public <E> void printArray(E[] arr) |
感觉泛型方法很方便,直接传入参数就可以了,不用管类型。但是这样势必会增大时间开销。
extends限定类型参数
例如:
<T extends 类名> 必须要是这个类或者子类 |
使用?通配符
前面已经说过,实例中泛型类型必须要和定义时泛型类型相同。例如:
Set<String> s1 = new HashSet<String>(); |
为了防止上述错误,可以使用通配符?,例如:
public class A |
print会出现问题,因为只能接受Object的泛型类型,后面一个就可以。
还可以和extends结合。例如:
TreeSet<? extends Number> x = new TreeSet<Integer>();//可以,Integer是Number的子类
这个和前面说的区别是前面是在定义时决定的,这个是在具体写代码时决定的,个人认为这种更为灵活。
还有super,后面类型只能是前面类型的父类或者他自己。例如:TreeSet<? super Integer> x = new TreeSet<Number>();
注意事项
- 看上去不同的泛型类型导致了不同的类。例如
A<Integer>
和A<String>
是不同的类,但是实际上泛型的类型在编译时期已经赋给内部的变量了,这个时候泛型被擦除了,所以实际上创建实例时还是调用了方法区中的A。 - 因为上一条,所以不允许出现名字相同只有泛型类型不同的重载,例如:
public void test(List<String> ls)
{
System.out.println("String");
}
public void test(List<Integer> ls)
{
System.out.println("Integer");
}
会报错 - 不能对确切的泛型使用instanceof符号,例如:
Collection cs = new ArrayList<String>(); cs instanceof Collection<String>; 报错
但是如果后面使用通配符就可以,cs instanceof Collection<?>通过
- 不能使用泛型类型进行强制类型转换,例如:
Collection cs = new ArrayList<String>();
ArrayList<String> list = (ArrayList<String>)cs;//会警告并且运行时容易出现异常