一、泛型的介绍
泛型可以理解为标签,集合容器在设计和声明阶段不能确定容器里到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计成Object,在JDK1.5之后可以使用泛型来解决
注意:泛型是我们在定义类和接口的时候,来规定这个类或者接口处理参数类型的一种手段,在我们设计类和接口的时候需要考虑。对于别人设计好的类或者接口,我们来使用就是要使用泛型,即传递类要处理的参数类型
使用泛型的好处:在编译时期就能进行类型检查保证数据的安全
二、总结集合的泛型使用
- 集合类或者集合接口在JDK1.5的时候都被改成带泛型的结构
- 在实例化集合类时,可以指明具体的泛型
- 在指明玩以后,在集合类或者接口中,凡是定义类或者接口时,内部结构使用到泛型的位置,都被指定为你实例化时的泛型类型比如add()方法等
- 注意泛型 必须是类,不能是基本数据类型
- 如果没有指明泛型类型,那么默认是java.lang.Object类型
三、自定义泛型结构
可以添加泛型接口的有:泛型类、泛型接口、泛型方法
3.1自定义泛型类 ```java package com.study; //自定义泛型类或者泛型方法:在类名或者接口名后面添加<泛型名(可以任意)>,在不能确定的类型后面使用泛型替代 //自定义泛型类(自定义泛型接口和泛型类差不多,把不能确定的类型使用泛型代替即可) public class Person{ private String name; //这个T就是Person类的一个成员变量,他和其他变量没有什么区别 //可以提供它的相关get/set方法等,唯一不同的是他的类型是只有在 //你创建对象时才能确定 private T orderT; public Person(){ } public Person(String name,T orderT){
} }this.name=name;
this.orderT=orderT;
**3.2自定义泛型方法**<br />**泛型方法和泛型类没有关系,不是用到了类定义的泛型的方法就是泛型方法,而是方法本身自己不确定内部接口而定义泛型,那么这个方法就是泛型方法,定义泛型方法是在声明方法的返回值前面加泛型结构,我们调用泛型方法是在方法名前加上具体的泛型**<br />**注意:泛型类中的静态方法是不能调用泛型结构的,但是静态方法可以定义成泛型方法**
```java
package com.study;
public class Man<E>{
//这个方法就是泛型方法,在返回值后面添加泛型结构(<泛型名>),
//这个泛型和类的泛型没有关系
public <T> void test(T a){
}
}
//泛型方法的调用
public class Test{
public static void main(String[] args){
Man man=new Man();
//在调用的时候可以指明泛型
man.<String>test();
}
//将静态方法定义成泛型方法
public static <K> void test1(){
}
}
总结:泛型类或者泛型接口定义是在类名和方法之后加上泛型结构(<泛型名>),泛型方法是在方法定义的返回值类型前加上泛型结构(<泛型名>),泛型方法和泛型类他们之间的泛型没有任何关系
3.3自定义泛型的注意事项
1.泛型可以是多个参数,例如Map,多个泛型之间使用逗号隔开
2.对于泛型类的构造器,不需要在加泛型接口(即
3.泛型不同的引用不能相互赋值(即虽然是同一个类型比如List类型,但是他们的泛型不同,也不能相互赋值,就相当于他们是两种类型)
4.静态方法不能使用泛型结构,因为静态方法它是随着类的加载而加载,在类加载的时候静态结构就会加载,但是泛型是只有创建对象才能确定,就是生命周期导致不能使用
5.自定义异常类不能加泛型(即继承Exception的类)
6.由于T它是泛型不是具体的对象,所以我们不能使用T[] arr=new T[10];//这样编译就不通过,因为不存在T这个类,但是我们可以用这种方式
T[] array=(T[])new Object[10];//通过这种方式我们在给数组赋值的使用必须赋T类型,或者T子类的对象
//我们在调用这个方法的时候,需要传递泛型,这个数据赋值的时候就需要传递我们赋的泛型类型
public class Man {
public <T> void test(){
T[] array=(T[])new Object[10];
}
}
四、子类继承泛型类的情况
4.1:情况一:子类继承带泛型的父类,在继承时指明泛型类型(那么子类将不再是泛型类)
package com.study;
//这里就是指继承泛型为String的Person类,那么我们在创建子类的对象时就不需要添加泛型,因为我们继承父类时,已经指明泛型,此时父类的泛型类型也就确定了,我们在调用相关泛型的方法时,泛型类型就是我们继承父类时指明的类型。即子类不再是泛型类
public class Student extends Person<String> {
}
public class Test{
public static void main(String[] args){
new Student();//创建对象时不需要指明泛型
}
}
情况二:子类继承泛型父类,但是没有指明父类的泛型(那么子类也是泛型类,在定义时需要加上泛型)
public class Sun<T> extends Person<T> {
}
public class Man<K,V,T> {
}
以上属于子类继承父类常见的泛型处理,具体还有:
4.2:有关泛型中的继承问题
两个集合的泛型具有子父类关系,这个两个集合不具有子父类关系,所以也不能使用多态
例如:
List<Object> list1=new ArrayList<>();
List<String> list2=new ArrayList<>();
//list2不能通过多态赋值给list1,他们不具有子父类关系
只有集合本身具体子父类关系,且泛型相同才可以使用多态
五、泛型通配符的使用
1.通配符 ?
由于泛型的关系,我们Java容器里放不同的结构,最后他们都不具有子父类关系,导致没法使用多态,但是我们可以使用通配符来解决
这样的缺点就是这个集合就能在添加数据了(null值除外),但是可以进行读取,读取的数据返回的是Object类型
List<String> list=new ArrayList<String>();
List<?> list1=new ArrayList<>();
list1=list;
//此时的List1虽然被list给赋值了,这个list1集合也不能往里添加数据了
2.有限制条件的通配符
由于通配符表示通配即什么都可以,但是我们可以添加通配的限制(添加上下线),extends规定上线,super规定下线
上线如果是一个类,那么通配的内容只能是该类的或者该类的子类的,上线如果是一个接口那么就是接口的实现
super规定下线,表示通配的内容是下线的父类
添加通配:可以实现集合中带泛型的多态,如果泛型在通配范围内,那么可以认为是子父类关系,可以进行赋值
List<? extends Object> list=new ArrayList<>();
List<String> list1=new ArrayList<>();
//list相当于list1的父类
list=list1;
List<? super String> list=new ArrayList<>();
List<Object>list1=new ArrayList<>();
list=list1;