发展过程

由父类转给子类时允许的,但是会抛出ClassCastException。这种异常是运行时异常,编译期不会检查,这就加大了检查的难度。为了解决这个问题,从jdk5开始引入了泛型。泛型可以把ClassCastException转换成编译时类型不兼容错误。

泛型符号是<>,里面可以使任意一种类(不能是int等基础类型,可以是Integer).

例如: Set<Object> set = new HashSet<Object>();//实例中的类型必须要和前面相同

泛型类,数组,接口,方法

泛型类

例:

public class Bag<T>
{
private T content;
public Bag(T content)
{
this.content = content;
}
public T get()
{
return this.content;
}

public void set(T content)
{
this.content = content;
}

public static void main(String[] args)
{
Bag<String> bag = new Bag<String>("mybook");
Integer content1 = bag.get();//出错
String content2 = bag.get();
}
}

这和c++中的模板十分类似。同样泛型参数可以有许多个,例如:public class A<str,inte,dou>{...}

注意 如果没有传入泛型实参的话,泛型变量可以使任何类型。例如:

Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);

Log.d("泛型测试","key is " + generic.getKey());
Log.d("泛型测试","key is " + generic1.getKey());
Log.d("泛型测试","key is " + generic2.getKey());
Log.d("泛型测试","key is " + generic3.getKey());

泛型接口

泛型接口和泛型类类似,但是它的实现类如果实现的是泛型接口,那么它的实现类也要是泛型。例如:

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)
{
for (int i = 0; i < arr.length; i++)
{
System.out.println(arr[i]);
}
}

public static void main(String[] args)
{
A a = new A();
Integer[] b = new Integer[3];
for(int i=0; i<3; i++)
{
b[i] = i;
}
a.printArray(b);
}

感觉泛型方法很方便,直接传入参数就可以了,不用管类型。但是这样势必会增大时间开销。

extends限定类型参数

例如:

<T extends 类名> 必须要是这个类或者子类
<T extends 接口名>

使用?通配符

前面已经说过,实例中泛型类型必须要和定义时泛型类型相同。例如:

Set<String> s1 =  new HashSet<String>();
Set<String> s2 = new HashSet<Integer>();//报错

为了防止上述错误,可以使用通配符?,例如:

public class A
{
public static void main(String[] args)
{
List<Integer> listInteger = new ArrayList<Integer>();
listInteger.add(11);
print(listInteger);
printNew(listInteger);
}
public static void print(Collection<Object> collection)
{
for(Object obj: collection)
{
System.out.print(obj);
}
}

public static void printNew(Collection<?> collection)
{
for(Object obj: collection)
{
System.out.println(obj);
}
}
}

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;//会警告并且运行时容易出现异常