泛型和通配符
记录一下自己学习内容,免得自己忘了
基础
泛型
集合容器类在设计声明阶段不能确定这个容器到底实际存取的是什么类型的对象,所以JDK 1.5之前只能把元素设计为Object类型,而在JDK1.5之后通过泛型解决这个问题。因为此时出克元素的类型不能确定以外,其他的部分都是确定的因此把元素的类型设计成一个参数,这个类型参数就叫做泛型,Collection,List,ArrayList,这个就是泛型
定义
所谓泛型,就是允许在定义类、接口时通过一个标识标识类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(继承和实现接口,用这个类型声明变量,创建对象时)确定(传入实际上午类型实参)
为什么要有泛型
1.解决元素存储的安全性问题,好比商品、药品标签、不会弄错
2.解决获取数据元素时,需要类型强制转换的问题,好比每回拿商品、药品都要辨别
在集合中使用泛型
package javaSE.Generic;
import java.util.*;
/**
*
* 泛型的使用 JDK 5.0新增的属性
*
* 在集合中使用泛型
* 总结:
* 集合接口在 JDK 5.0时全部修改为带泛型的
* 在是实例化集合类时可以指明具体的泛型类型
* 指明完以后,在集合类或接口中凡是定义类和接口时,内部结构使用到类的泛型的位置,都指定为实例化的泛型
* 泛型的类型必须是类,不是基本类型,需要用到基本数据类型的位置,拿包装类替换
* 如果实例化时没有指定泛型类型,默认为Object类型
* @Author: Thewind
* @Date: 2021/1/31 15:09
*/
public class GenericTest {
public static void main(String[] args) {
Test test = new Test();
// test.test1();
// test.test2();
test.test3();
}
}
class Test{
//在集合中使用泛型之前
public void test1(){
ArrayList list = new ArrayList();
//需求;存储一些列int数据
list.add(12);
list.add(25);
list.add(8);
list.add(21);
//问题一,类型不安全
list.add("Tom");
for (Object score : list){
//问题二、强转时会出现ClassCastException,因为Tom能转换为Integer类型
int stuScore = (int)score;
System.out.println(stuScore);
}
}
//在集合中使用泛型的情况
public void test2(){
ArrayList<Integer> score = new ArrayList<>();
//编译时会进行类型检查,保证数据安全
//score.add("Tom"); 编译不通过,报错
score.add(12);
score.add(25);
score.add(8);
score.add(21);
for (Integer sc : score){
//避免了强转
System.out.println(sc);
}
}
//在集合中使用泛型:以HashMap为例
public void test3(){
HashMap<String, Integer> score = new HashMap<String, Integer>();
score.put("Tom" , 94);
score.put("Jerry" , 87);
score.put("Mary" , 96);
score.put("Rose" , 81);
Set<Map.Entry<String, Integer>> entries = score.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> next = iterator.next();
String key = next.getKey();
Integer value = next.getValue();
System.out.println("key = " + key + ", value = " + value);
}
}
}
自定义泛型结构
自定义泛型结构包括:泛型类、泛型接口、泛型方法
package javaSE.Generic;
/**
*
* 自定义泛型结构;泛型类,泛型接口,泛型方法
* 我们定义了一个泛型类
*
*
* @Author: Thewind
* @Date: 2021/1/31 16:51
*/
public class GenericTest1 {
public static void main(String[] args) {
Test1 test1 = new Test1();
test1.test1();
}
}
class Test1{
public void test1(){
//如果为实例化定义的发现性类,默认泛型类型为Object类型
//要求,如果定义了泛型类,使用时一定要指明类型实参
Order<String> order = new Order<>("OrderAA", 1001, "韩释毅");
System.out.println(order);
}
}
package javaSE.Generic;
/**
*
* 泛型类Order
*
* @Author: Thewind
* @Date: 2021/1/31 16:36
*/
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 T getOrderT() {
return orderT;
}
public void setOrderT(T orderT) {
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
}
泛型特点
1.泛型类可能有多个参数,此时应将多个参数一起放在尖括号内,比如<E1, E2, E3>
2.泛型类的构造器如下:public GenericClass(){},而不是public GenericClass(){}
3.实例化后操作原来泛型位置的结构必须与指定的泛型类型一致
4.泛型不同的引用不能相互赋值
>尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时 只有一个ArrayList被加载到JVM中
5.泛型如果不指定,将会被擦除,泛型对应的类型均按Object处理,但不等价与Object
经验:泛型要用就一路都是用,要不用就都不用
6.如果泛型结构是一个接口或者是抽象类,则不可创建泛型类的对象
7.JDK 1.7,泛型的简化操作:ArrayList first = new ArrayList<>();
8.泛型的指定不能用基本数据类型,可以使用包装类替换
9.在类/接口上声明的泛型,在本类或接口中即代表某种类型,可以作为非静态属性的类型、非静态参数方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用泛型
10.异常类不能是泛型的
11.不能使用 E[] elements = new E[],但是可以用E[] elemrnts = (E[]) new Object[capacity];(强转)
参考:ArrayList源码声明中Object[] elementData,而非泛型参数类型整数
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型
子类不保留父类的泛型:
没有类型,擦除
具体类型
子类保留父类的的泛型,
全部保留
部分保留
结论:子类必须是富二代,子类除了指定或者保留父类的泛型,也可以拥有自己的泛型
class Father<T1,T2>{
}
//子类不保留父类泛型
//1.没有类型,擦除
class Son1 extends Father{//等价于class Son extends Father<Object,Object{
}
//2.具体类型
class Son2 extends Father<Integer,String>{
}
//子类保留父类泛型
//1.全部保留
class Son3<T1,T2> extends Father<T1,T2>{
}
//2,保留部分
class Son3<T2> extends Father<Integer,T2>{
}
泛型类的继承
* 情况一,继承时直接指定泛型类型,如下
* public class SubOrder extends Order<Integer> {
* }
*此时定义该子类的对象时,不需要指明泛型类型,如下
* SubOrder sub = new SubOrder();此时使用此对象的方法时使用的就是继承时
* 指定的泛型类型,此时SubOrder就不再是一个泛型类,只是一个普通的类
*
* 情况二,继承时将子类也定义为泛型类,方式如下
* class SubOrder1<T> extends Order<T>{
* }
* 因为父类是泛型类且未指定实例参数,所以必须把子类也定义为泛型类,因为子类继承
* 的父类方法可能也是泛型方法
* 此时指定子类的实例对象时要指定实例参数,如下所示
* SubOrder<String> sub2 = new SubOrder<>();
泛型方法:在方法中出现了泛型结构,泛型参数与类的泛型参数没有任何关系
换句话说泛型方法所属的类是不是泛型类都无所谓
//最前面的<E>是为了让编译器理解E是范性参数,而不是一个类
public <E> List<E> copyFromArryToList(E[] arr){
ArrayList<E> list =new ArrayList<>();
for(E e : arr){
list.add(e);
}
}
注意:泛型方法可以声明为静态,原因:泛型参数是在调用方法时确认,而不是在实例化时确定
泛型在继承方面的体现
/**
*泛型在继承方面的体现一
*类A是类B的父类,但G<A>和G<B>二者不具备父子关系,二者是并列关系
*A是B的父类,A<G>是B<G>的父亲
*/
public void test(){
Object object = null;
String str = null;
obj = str;
//允许,因为String和Object具有继承关系
Object[] arr1 = null;
String[] arr2 = null;
arr1 = arr2;//允许
List<Object> list1 = null;
List<String> list2 = new ArrayList<String>();
//此时list1和list2不具有继承关系,故list1 = list2不允许
//因为虽然String是Object的子类,但是List<String>与List<Object>之间没有继承关系
}
通配符的使用
通配符:?
A是B的父类,G< A > 和 G< B> 是没有关系的,二者共同的父类是:G<?>
public void test(){
List<Object> list1 = null;
List<String> list2 = null;
List<?> list= null;
list = list1;
list = list2;
print(list1);
print(list2);
//
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
list = list3;
//添加:对于List<?>就不能向其内部添加数据。除了添加null之外,因为所有的类型对象都可以加载null
//list.add("DD");//报错
//读取:允许读取数据
Object o = list,get(0);
//之所以是Object类型的数据是因为get返回的是一个?类型的数据,其总是可以复制给一个Object类型的数据
}
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while(iterator.hanext()){
? = iterator.next();
}
}
有限制条件的通配符
<?>允许所有泛型的引用调用通配符指定上限
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
通配符指定下限
下限super:使用时指定的类型不能小于操作的类,即>=
举例
<? extends Number> (无穷小,Number]只允许泛型为Number和Number子类的引用调用
<? super Number> [Number,无穷大) 只允许泛型为Number及Number父类的引用调用
<? extends Comparator> 只允许泛型为实现Comparator接口的实现类的引用
public void test(){
//Student是Person的子类
List<? extends Person> list1 =null;
List<? super Person> list2 =null;
List<Student> list3 =null;
List<Person> list3 =null;
List<Object> list3 =null;
list1 = list2;
list1 = list3;
// list1 = list4;失败,超出上限
//list1 = list2;失败,低于下限
list1 = list3;
list1 = list4
}