是什么:是一个标签。
就像超市购物架上很多瓶子,每个瓶子装的是什么,有标签。
设计背景:
集合容器类在声明阶段不能确定这个容器到底存的是声明类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的。因此此时把元素设计成一个参数,这个类型参数叫做泛型。
Collection,List,ArryaList
这个就是类型参数,即泛型。
概念:
1、在定义类、接口时候通过一个标识类中:某个属性的类型,或者是某个方法的返回类型及参数类型。
2、这个类型参数在使用时,确定(传入实际的类型参数,也成为类型实参)。
Ⅰ、使用时:例如,继承或者实现这个接口,用这个类型声明变量、创建对象时
Ⅱ、确定:传入实际的类型参数,也成为类型实参
一、集合中使用泛型
1、优势:
Ⅰ、解决元素存储的安全问题。
Ⅱ、获取元素时候,需要类型强制转换的问题。
没有泛型时候的问题:
public class GenericTest {
//在集合中使用泛型之前的情况
@Test
public void test1(){
ArrayList list = new ArrayList();
//需求:存放学生的成绩
list.add(78);
list.add(76);
list.add(89);
list.add(88);
//问题一:类型不安全
list.add("Tom");
for(Object score:list){
//问题二:强转时,可能出现ClassCastException
int stuScore = (Integer) score;
System.out.println(stuScore);
}
}
}
78
76
89
88
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
使用泛型:编译的时候过不去了。
可以在编译的时候进行类型检查,不是所有的数据都能进进来的。
@Test
public void test3(){
HashMap<String, Integer> map = new HashMap<>();
map.put("Tom", 87);
map.put("Jerry", 87);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
System.out.println(e);
}
}
①集合接口或者集合类在jdk5.0的时候都修改为带泛型的结构
②在实例化集合类的时候,可以只能具体的泛型类型。
③指明完成后,在集合类或者接口中凡是定义类或者接口时候,内部结构
比如:方法构造器…add(E e)—>实例化以后:add(Integer e)
④注意点:泛型的类型必须是类,不能是基本数据类型。需要用的时候用包装类替换。
⑤如果实例化时候,没有指明泛型的类型,默认类型为java.lang.Object类型
二、自定义泛型
1、泛型类可以有多个参数,此时应将多个参数一起放在尖括号内。比如<E1,E2,E3>
2、泛型类的构造器如下:public GenericClass(){}。
不能是public GenericClass(){}
3、实例化后,操作原来泛型位置的结构必须与指定的泛型的类型一致。
4、泛型不同的引用不能相互赋值。
5、泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
6、如果泛型结构是一个接口或者抽象类,则不可创建泛型类的对象
7、jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();
8、泛型的指定中不能使用基本数据类型,可以使用包装类替换。
9、在类/接口中声明的泛型,在本类或本接口中即代表某中类型,可以作为非静态属性的类型,非静态方法
返回值类型。注:在静态方法中不能使用类的泛型。
10、异常类不能使用泛型。
>
11、不能使用new E[]。但可以:E[] elements = (E[])new Object[capacity];
ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
public class Order<T> {
String orderName;
int orderId;
T orderT;
public Order() {
//编译不通过,
//T[] arr = new T[10];
//实际数组的元素类型是Object类型,
//但是,具体放数据的时候只能往里面写T和子类类型,否则编译不通过
T[] arr = (T[])new Object[10];
}
12、父类有泛型,子类可以选中保留泛型也可以选中指定泛型类型。
子类不保存父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留、部分保留
结论:子类必须是富二代,子类除了指定或者暴露父类的泛型,还可以增加自己的泛型
class Father<T1,T2>{
}
//1、子类不保留父类的泛型
//(1)没有类型,擦除
class Son1 extends Father{
//等价于class Son extends Father<Object,Object>{}
}
class Son<A,B> extends Father{
//等价于class Son extends Father<Object,Object>{}
//在自己类型中可以用自己的参数
}
//(2)具体类型
class Son2 extends Father<Integer,String>{}
class Son22<A,B>extends Father<Integer,String>{}
//2、子类不保存父类的泛型
//(1)全部保留
class Son3<T1,T2> extends Father<T1,T2>{ }
class Son31<T1,T2,A,B>extends Father<T1,T2>{}
//(2)部分保留
class Son4<T2> extends Father<Integer,T2>{ }
class Son41<T2,A,B>extends Father<Integer,T2>{}
1、 泛型类/接口:类/接口的内部结构就可以使用类的泛型
public class Order<T> {
String orderName;
int orderId;
T orderT;
public Order() {
//编译不通过
//T[] arr = new T[10];
T[] arr = (T[])new Object[10];
}
public T getOrderT(){
return orderT;
}
public Order(String orderName, int orderId, T orderT) {
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public void setOrderT(T orderT) {
this.orderT = orderT;
}
// //静态方法不能使用类的泛型。因为实例化以前参数没有确定
// public static void show(T orderT){
// System.out.println(orderT);
// }
}
2、泛型方法:
1、在方法中出现了泛型的结构,泛型参数于类的泛型参数没有任何关系。
2、泛型方法所属的类是不是泛型类都没有关系。
3、泛型方法,可以声明为静态的。原因:泛型参数是在调用方法的时候确定的。并非在实例化时候确定的。
List copyFromArrayToList(E[] arr)
在List前写为了说明:不是有一个类叫做E,而是有个参数是E。
如果不加E,说明E是个类型
public class Order<T> {
public <E>List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for (E e:arr){
list.add(e);
}
return list;
}
}
public static void main(String[] args) {
Order<String> order = new Order<>();
Integer[] arr = {1, 2, 3, 4};
//泛型方法在调用的时候,指明泛型参数的类型
List<Integer> list = order.copyFromArrayToList(arr);
for (Integer i:list){
System.out.println(i);
}
}
3、使用泛型情况:
场景:定义了一个操作数据库表的统一操作类,但是没有具体定义操作那一张表。用泛型类的类型变量来标记未知。
public class DAO<T> {
//增加一条记录
public void add(T t){
}
//查询一条记录
public T getIndex(int index){
return null;
}
}
具体的表映射的对象.
public class Customer {
}
Customer表的操作
public class CustomerDao extends DAO<Customer>
{
@Override
public void add(Customer customer) {
super.add(customer);
}
@Override
public Customer getIndex(int index) {
return super.getIndex(index);
}
public static void main(String[] args) {
CustomerDao customerDao = new CustomerDao();
customerDao.add(new Customer());
}
}
4、通配符使用:
① 通配符:?
类A是类B的父类,G和G是没有关系的,两者共同的父类是:G<?>
public void test1(){
Object obj = null;
String str =null;
obj = str;
Object[] arr1 =null;
String[] arr2 = null;
arr1 =arr2;
List<Object> list1 = null;
List<String> list2 = null;
//此时的list1和list2的类型不具有子父类关系
// 所以不能赋值。编译不通过
/*list1=list2;*/
/*
* 反证法:
* 假设list1=list2;
* list1.add(123);
* 导致混入非String的数据。
* */
}
② 使用通配符后数据的读取与写入
Ⅰ、写入:list<?>就不能向内部添加数据
Ⅱ、获取:读取的数据类型为Object。
public void test3(){
List<Object> list1 = null;
List<String> list2 = null;
/*解决泛型是两者是并存的关系,增加一个公共父类*/
List<?> list = null;
list = list1;
list = list2;
ArrayList<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
//(1)写入:对于list<?>就不能向内部添加数据
//除了添加null之外。
list=list3;
// list.add("DD");
list.add(null);
//(2)读取:读取的数据类型为Object。
Object o = list.get(0);
System.out.println(o);
}
③有限制调节的通配符
Ⅰ、通配符的上限:
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
Ⅱ、通配符的下限
下限super:使用时指定的类型不能小于操作的类,即>=
Ⅲ、举例:
<? extends Number> (无穷小,Number]
只允许使用泛型为Number及Number子类的引用调用
<? super Number> [Number,无穷大)
只允许泛型为Number及Number父类的引用调用
class Man extends Person{
}
class Person{
}
public void test4(){
List<? extends Person> list1 =null;
List<? super Person> list2 = null;
List<Object> list3 = null;
List<Man> list4=null;
list1=list4;
list2=list3;
}