泛型
城市中的垃圾桶上面会标识该垃圾桶可放入的垃圾,例如垃圾筒上标识可回收垃圾,此时“可回收垃圾”就是该垃圾桶这个容器的标签。
泛型的定义
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时确定。
泛型可以理解成标签
把元素的类型设计成一个参数,这个类型参数叫做泛型
eg:Collection< E >,List< E >,ArrayList< E >
这个< E >就是类型参数,即泛型
如果在集合中没有使用泛型会出现的问题:
- 类型不安全
- 强转时,可能出现ClassCastException异常
在集合中使用泛型
- 编译时会进行类型检查,保证数据的安全
- 避免了强转操作
在集合中泛型的使用
1、 集合接口或集合类在jdk5.0时都修改为带泛型的结构
2、 在实例化集合类是可以指明具体的泛型类型
标准写法:
> Map<String,Integer> map = new HashMap<String,Integer>();
可以写成(类型推断):
> Map<String,Integer> map = new HashMap<>();
3、 指明结束后,在集合类或是接口中凡是定义类或接口时,内部结构(方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化时泛型的类型
开始时的add()方法
> add(E e)
实例化后add()方法
> add(Integer e)
4、 泛型的类型必须是一个类(不可以是基本数据类型,需要使用基本数据类型的时候,使用包装类)
5、 如果实例化时,没有指明泛型的类型,默认类型为Object
代码实现
年龄从小到大排列
public void test2(){
//想给谁进行排序,泛型就是谁
Comparator<Employee> com = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return Integer.compare(e1.getAge(),e2.getAge());
}
};
TreeSet<Employee> set = new TreeSet<>(com);
Employee e1 = new Employee("Ann",20,new MyDate(2000,5,6));
Employee e2 = new Employee("Tom",21,new MyDate(1999,10,26));
Employee e3 = new Employee("Jack",22,new MyDate(1998,9,3));
Employee e4 = new Employee("Lisa",19,new MyDate(2001,1,15));
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
Iterator<Employee> iterator = set.iterator();
while (iterator.hasNext()){
Employee employee = iterator.next();
System.out.println(employee);
}
}
自定义泛型结构
自定义泛型类、泛型接口
具体写法
public class Order<T> {
String orderName;
int orderId;
//类的内部结构,可以使用类的泛型
T orderT;
public Order(){
}
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 T getOrderT() {
return orderT;
}
public void setOrderT(T orderT) {
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
}
使用该自定义的泛型类
public void test1(){
Order<String> order1 = new Order<>();
order1.setOrderT("AA");
Order<Integer> order2 = new Order<>();
order2.setOrderT(123);
}
说明
1、 如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
2、 如果定义了类带泛型,建议实例化时要指明类的泛型
3、 子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型
//此时SubOrder不再是一个泛型类
public class SubOrder extends Order<Integer>{
}
SubOrder subOrder = new SubOrder();
subOrder.setOrderT(123);
4、 子类同样是泛型类的写法
public class SubOrder1<T> extends Order<T>{
}
SubOrder1<String> subOrder1 = new SubOrder1<>();
subOrder1.setOrderT("AA");
注意点
1、 泛型类可能有多个泛型,此时应将多个参数一起放入尖括号中
> <E1, E2, E3>
2、 泛型类的构造器(举例如下)
> public Order()\{\}
不是
> public Order< T >()\{\}(错误❌)
3、 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致
4、 泛型不同的引用不能相互赋值
5、 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object
6、 泛型要使用一路使用,要不用,一路都不要用
7、 如果泛型结构是一个接口或抽象类,则不可以创建泛型结构的对象
8、 静态方法中不能使用类的泛型
因为静态结构早于对象的创建,不可以还没有指定泛型静态结构就要去使用,所以静态方法不能使用类的泛型
9、 异常类不能声明为泛型的
10、 不可以使用
> new T\[\](编译不通过)
可以使用
> T\[\] arr = (T\[\]) new Object\[10\];
自定义泛型方法
泛型方法
- 在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
- 泛型方法所属的类是不是泛型类都没有关系(不是泛型类也可以使用)
- 泛型方法,可以声明为静态的,因为泛型参数是在调用方法时确定的,并非是咋实例化类时确定的
自定义泛型方法格式
public <E> List<E> copyFromArrayToList(E[] arr){
}
举例
public <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
泛型方法在调用时指明泛型参数的类型
Order<String> order = new Order<>();
Integer[] arr = new Integer[]{1, 2, 3, 4};
List<Integer> integers = order.copyFromArrayToList(arr);
泛型在继承方面的体现
- 虽然类A是类B的父类,但是G< A >和G< B >二者不具备子父类关系,二者为并列关系
//此时list1和list2的类型不具有子父类关系
List<Object> list1 = null;
List<String> list2 = null;
list1 = list2;//编译不通过
- 类A是类B的父类,A< G >是B< G >的父类
List<String> list1 = null;
ArrayList<String> list2 = null;
list1 = list2;
通配符的使用
通配符:?
类A是类B的父类,G< A >和G< B >没有关系(两者并列),二者共同的父类是G< ? >
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
使用通配符后数据的读取和写入要求
- 写入过程:对于List< ? >来讲,不能向其内部添加数据,但是可以添加null
- 读取过程:允许读取数据,读取的数据类型为Object
List<String> list3 = new ArrayList<>();
list3.add("AA");
list = list3;
//写入
//list.add("BB");//报错
//读取
Object o = list.get(0);
System.out.println(o);//AA
有限制条件的通配符的使用
? extends A
G<? extends A>
可以作为G< A >和G< B >的父类,其中B是A的子类(?可以是A或者A的子类)
? super A
G<? super A>
可以作为G< A >和G< B >的父类,其中B是A的父类(?可以是A或是A的父类)
代码举例
List<? extends Person> list1 = null;
List<? super Person> list2 = null;
List<Student> list3 = null;
List<Person> list4 = null;
List<Object> list5 = null;
list1 = list3;
list1 = list4;
//list1 = list5;//不可以
//list2 = list3;//不可以
list2 = list4;
list2 = list5;