一个被举了无数次的例子:
List arrayList = new ArrayList(); arrayList.add("aaaa"); arrayList.add(100); for(int i = 0; i< arrayList.size();i++){ String item = (String)arrayList.get(i);//此处会抛出ClassCastException Log.d("泛型测试","item = " + item); }
我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。
List<String> arrayList = new ArrayList<String>(); ... //arrayList.add(100); 在编译阶段,编译器就会报错
在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,泛型信息不会进入到运行时阶段。
泛型的使用
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
泛型类
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型 //在实例化泛型类时,必须指定T的具体类型 public class Generic<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key; public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 this.key = key; } public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key; } }
//传入的实参类型需与泛型的类型参数类型相同,即为Integer. Generic<Integer> genericInteger = new Generic<Integer>(123456);
Generic generic = new Generic("111111");
未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 class FruitGenerator<T> implements Generator<T> class FruitGenerator implements Generator<String> // 如果不声明泛型,如: class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
泛型通配符
通配符?:相当于Object可以放任何类型。可以解决当具体类型不确定的时候,这个通配符就是 ? 。
public void showKeyValue1(Generic<?> obj){ Log.d("泛型测试","key value is " + obj.getKey()); }
限制条件的通配符的使用
泛型方法
权限修饰符与 返回值中间非常重要,可以理解为声明此方法为泛型方法。只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。方法体中使用到泛型的位置都指定为调用方法时的泛型类型。
public <T> T genericMethod(Class<T> tClass)throws InstantiationException , IllegalAccessException{ T instance = tClass.newInstance(); return instance; } Object obj = genericMethod(Class.forName("com.test.test"));
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
类中的泛型方法
当然这并不是泛型方法的全部,泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的,当泛型方法出现在泛型类中时,我们再通过一个例子看一下
public class GenericFruit { class Fruit{ @Override public String toString() { return "fruit"; } } class Apple extends Fruit{ @Override public String toString() { return "apple"; } } class Person{ @Override public String toString() { return "Person"; } } class GenerateTest<T>{ public void show_1(T t){ System.out.println(t.toString()); } //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。 //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。 public <E> void show_3(E t){ System.out.println(t.toString()); } //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。 public <T> void show_2(T t){ System.out.println(t.toString()); } } public static void main(String[] args) { Apple apple = new Apple(); Person person = new Person(); GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>(); //apple是Fruit的子类,所以这里可以 generateTest.show_1(apple); //编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person //generateTest.show_1(person); //使用这两个方法都可以成功 generateTest.show_2(apple); generateTest.show_2(person); //使用这两个方法也都可以成功 generateTest.show_3(apple); generateTest.show_3(person); } }
泛型方法与可变参数
再看一个泛型方法和可变参数的例子:
public <T> void printMsg( T... args){ for(T t : args){ Log.d("泛型测试","t is " + t); } } printMsg("111",222,"aaaa","2323.4",55.55);
静态方法与泛型
类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。
public class StaticGenerator<T> { public static <T> void show(T t){ } }