1、泛型的概念
- 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。
- 泛型就是指允许在定义类、接口时通过一个标识符表示类的类型或是某个方法的返回值参数类型。这个类型参数在使用时(声明变量、创建对象)时确定。
为什么要使用泛型
2、在集合中使用泛型
- 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
ArrayList<Integer> arr=new ArrayList<>();
arr.add(78);//添加成功
arr.add("ABC");//添加失败,只能添加int类型数据
//进行类型检查,保证数据安全性
- 集合类或集合接口在JDK5.0时都修改为带泛型的结构
- 集合类或接口定义时,内部结构使用到泛型结构都指定为实例化的泛型类型 =》 静态方法不能使用泛型
- 实例化时如果没指明泛型的类型,默认都是java.lang.object类型
- JDK 7允许
Map<String,Integer> map=new Map<String,Integer>();
//JDK7之后就允许变成
Map<String,Integer> map=new Map<>();
3、自定义泛型
- 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
- 泛型类的构造器
- public Person () {} //true
- public Person< E > () {} //false
- 泛型不同的引用不能相互赋值。
- ArrayList< Integer > 和ArrayList< String > 不能相互赋值
- 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
- 泛型要使用一路都用。要不用,一路都不要用。
- 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
- 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
- 静态方法不能使用泛型(泛型在实例化时才实现)
- 异常类不能使用泛型
T[] arr=new T[]{}; //错误
T[] arr=(T[]) new Object[]{}; //正确
泛型方法
- 格式
- 权限修饰符 + 泛型类型 +返回值类型 +方法名(形参)
public <E> list<E> get(E[] arr){}
- 在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
- 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
package test6;
import java.util.ArrayList;
import java.util.List;
public class fanxing {
public static void main(String[] args) {
Person<String, Integer> per = new Person<>("Tom", 20);
System.out.println(per.getAge());
ArrayList<Integer> arr=new ArrayList<>();
arr.add(15);
arr.add(201);
List<Integer> list=Person.get(arr);
for(Integer i:list){
System.out.println(i);
}
}
}
class Person<E, V> {
E name;
V age;
public Person(E name, V age) {
this.name = name;
this.age = age;
}
public E getName() {
return name;
}
public V getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name=" + name +
", age=" + age +
'}';
}
public static <K> List<K> get(List<K> arr) {
ArrayList<K> array = new ArrayList<>();
for (K e : arr) {
array.add(e);
}
return array;
}
}
4、关于子类保留父类泛型
class father< T1, T2 >{}//父类
//子类
//子类不保留 (son1,son2不再是泛型)
class son1 extends father{} 等价于class Son extends Father<Object,Object>{}
//子类保留,并指定具体类型
class son2 extends father<String,Integer>{}
//子类全部保留
class son3< T1,T2> extends father<T1 ,T2>{}
//子类部分保留
class son4<T1> extends father<T1,Integer>(){}
//子类定义自己的泛型
class son5<T1,T2,T3,T4> extends father<T1,T2>{}
结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型。
5、泛型在继承上的体现
- 类A是类B的父类 ,但G< A >,G< B > 不具备子父类关系,二者是并列关系
- 类A是类B的父类,A< T > 是 B < T >的父类
通配符
- 通配符:?
- 类A,类B是子父类关系,G< A >,G< B >没有关系,二者有相同的父类G< ? >
- List<?> list为例
- 添加操作是不允许的,因为不知道list中是什么元素类型
- 除了null以外,null是所有类的成员
- 读取操作是允许的,指定接收类型为Object,因为它是所有类的父类
- Object obj =list.get( 0);
- 我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。
- 添加操作是不允许的,因为不知道list中是什么元素类型
有限制条件的通配符的使用
- <?>
- 允许所有泛型的引用调用
-
通配符指定上限
- 使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
- ? extends A (A为类、接口)
- (无穷小, A] 只允许泛型为A及A子类的引用调用
-
通配符指定下限
- 使用时指定的类型不能小于操作的类,即>=
-
- ?super A(A为类)
- [A , 无穷大)只允许泛型为A及A的父类的引用调用