一、泛型出现的背景
早期Java是使用Object来代表任意类型的,这样使用会出现两个问题:
- Collection、Map集合对元素的类型无法做限制。本来我的Collection集合想装载的只能是Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
- 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object,还需要强制转换。
为了解决这两个问题,泛型应运而生,我们通过泛型可以限制某个类(常应用在集合类中)中元素的类型,并且get时不需要进行强转。
二、自定义一个简单的泛型类
public class GenericTest<T> {
private T genericValue;
public T getGenericValue() {
return genericValue;
}
public void setGenericValue(T genericValue) {
this.genericValue = genericValue;
}
public static void main(String[] args) {
GenericTest<String> stringGenericTest = new GenericTest<>();
// 1、只能set String类型的值
stringGenericTest.setGenericValue("123");
// 2、存放整形的值会报错
// stringGenericTest.setGenericValue(123);
// 3、取出的值不需要强转
String stringValue = stringGenericTest.getGenericValue();
System.out.println(stringValue);
// 4、且必须用String来接收,不然会报错
// Integer integer = stringGenericTest.getGenericValue();
}
}
三、注意事项
- 什么是类型擦除?
Java中的泛型是在编译器这个层次来实现的,生成的Java字节码中是不包含泛型的类型信息的。而泛型的类型参数,会在编译器在编译的时候去掉,这个过程就称为类型擦除。
如:
第五条:在代码中定义的ArrayList和ArrayList类型,在编译后都会变成List。
第六条:通过反射可以在Integer集合中的Integer类型被擦除后存放字符串
// 5、泛型的类型擦除证明:两个ArrayList编译后的类型是相同的
ArrayList<String> stringArrayList = new ArrayList<>();
stringArrayList.add("123");
ArrayList<Integer> integerArrayList = new ArrayList<>();
integerArrayList.add(123);
System.out.println(stringArrayList.getClass() == integerArrayList.getClass());
// 6、类型擦除的证明:通过反射可以跳过编译阶段的类型检测将字符串存到整形集合中
integerArrayList.getClass().getMethod("add",Object.class).invoke(integerArrayList,"我是字符串");
System.out.println(integerArrayList.get(1));
更多细节参考大神博文:泛型的类型擦除
- 引用和内存中的实例
下面的代码中,集合"arrayList"和“linkedList”的引用类型都是List,内存中的实例分别是“ArrayList”和“LinkedList”,需要注意的是我们在使用的时候调用的是引用类型List的相关方法,跟内存中的实例是什么无关。
List arrayList = new ArrayList();
List linkedList = new LinkedList();
泛型的限制也是如此,
// 泛型声明在实例对象中不起作用
ArrayList arrayList = new ArrayList<Integer>();
// 泛型声明在引用中可以起作用
ArrayList<Integer> genericArrayList = new ArrayList<>();