Java泛型知识点总结
一、为什么要使用泛型
同一段代码,可以被很对不同类型的对象所重用,但我们并不希望为两个不同的对象设计不同的代码块。以ArrayList为例,AraryList可以存储任何类型的对象,JDK会为每一种类型都写一个ArrayList类吗??显然不可能。
那Java语言如何解决这个问题呢?在JavaSe 5.0之前,ArrayList类维护一个Object类的数组,如下
public class ArrayList{
private Object [] elementDate;
public Object get(int i){...}
//在一个Object类型的数组下进行操作
public void add(Object o){...}
...
}
这样实现有个问题,当获取一个值时必须进行强制类型转换
ArrayList files = new ArrayList();
String str = (String)files.get(2);
对于这个强转,编译不会报错,但运行时会出现转换异常:java.lang.ClassCastException,这给程序员带来很大困扰。所以在Java5之后,从 Java 5 之后,Java 引入了参数化类型parameterized type
的概念。ArrayList类有一个类型参数指示元素类型。
ArrayList<String> files = new ArrayList<String>();
这使得代码大大提高可读性,让人们一眼就看出来操作的是String类型的对象。
借助泛型,可以事先指定 List 中的元素类型,这样编译器在编译阶段就能够发现问题。如下图
二、泛型实例
1、泛型类
1.1泛型类的定义语法
class 类名称<泛型标识>{
private 泛型标识 成员变量名;
……
}
泛型标识
可以随意指定,尽量使用单个大写英文字母。
但尽量做到见名知意,常用有以下几个:
T:type
E:Element
K:key
V:value
1.2 泛型类实例演示
public class Generic<T> {
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public static void main(String[] args) {
//注意:泛型的类型参数只能是类类型(包括自定义类),不能是基本数据类型。
Generic<Integer> generic = new Generic<>(10);
Integer value = generic.getKey();
System.out.println(value);
//传入的实参类型也要和泛型的类型参数相同。即String类型
Generic<String > generic1 = new Generic<>("我爱Java");
String str = generic1.getKey();
System.out.println(str);
}
}
思考:定义了泛型类就一定要传入泛型类型参数吗?
并不是这样,如果不传入泛型类型参数,在泛型类中使用泛型的方法或成员变量定义的类型可以是任何类型,例如:
ArrayList list = new ArrayList();
等价于:ArrayList<Object> List = new ArrayList<>();
1.3泛型类的注意事项
●对类型进行自动检查注意:不是替换仅仅是在编译期用来进行类型安全检查! !! ! ! !
●自动对类型进行类型转换
●泛型的类型参数只能是引用数据类型,不能是基本数据类型。
●不能对确切的泛型类型使用instanceof操作。
●不能直接new泛型数组。
●不能产生泛型类型对象。
●在static方法中,是不能用泛型类型参数的。因为static方法是不依赖对象存佐的,所以无法排知static泛型参数类型。
2、泛型接口
泛型接口的定义和实例演示
public interface Generator<T>{
T next();
}
、、、
class FruitGenerator<E> implements Generator<E>{
@Override
public E next(){
return null;
}
接口实现类定义时,也需要将泛型的声明加上。如果不加,例如public class FruitGenerator implements Generaotr<T>,这样就会报错,cannot resolve symbol T
。
//泛型接口实现类,传入泛型实参,比如 String
public class FruitGenerator implements Generaotr<String> {
此时,接口实行类就不用加上泛型声明了,加入的话,反而会提示 attempting to use incompatible return type
错误。
3、泛型方法
我们在此之前看到的泛型都是应用在整个类上,但方法同样可以包含参数化方法,而这个方法所在的类可以是泛型类也可以不是泛型类。也就是说,是否拥有泛型方法与其所处的类是否是泛型类毫无关系。方法中,通过指定类型参数,可以声明泛型方法,从而提高其可复用性。泛型方法的参数化类型列表放在方法类符的后面返回值之前。
修饰符 <T,S,...> 返回值类型 方法名(形参列表){
// 方法体
}
public genericTest{
//只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
//<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
public <T> String genericMethod(Class<T> gClass){
return gClass.getName();
}
public static void main(String[] args) {
String result = genericMethod(String.class);
System.out.println(result);
}
}
注意:
如果泛型方法的泛型参数与类的泛型参数相同,编译器会发生警告。
public class Generic<T> {//泛型类
public <T> void show(){//泛型方法。此时会在<T>处发出警告。
System.out.println("你好");
}
三、类型擦除
定义:Java的泛型是伪泛型,为什么称为伪泛型呢?因为在编译期间,所有的泛型信息都会被擦除掉。Java 中的泛型基本上都是在编译期这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
import java.util.ArrayList;
public class TestDemo{
public static void main(String[] args) {
ArrayList< String> arrayList1=new ArrayList<String> ();
arrayList1.add("abc");
ArrayList < Integer> arrayList2=new ArrayList< Integer> ();
arrayList2.add(123);
System.out.printIn(arrayList1.getClass()== arrayList2.getClass();
}
}
我们通过arrayList1 对象和arrayList2对象的getClass方法获取
它们的类的信息,最后发现结果为true。
说明泛型类型String和Integer都被擦除掉了,只剩下了原
始类型(Object类型)。