泛型概述
泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型,这个类型参数将在使用时确定(通过标识代替的只能是类,不能是基本数据类型)。这个机制允许我们在创建集合时再指定集合元素的类型。如List<String>,这表明该List只能保存字符串类型的对象。
泛型的核心思想就是:把一个集合中的内容限制为一个特定的数据类型
把元素的类型设计成一个参数,这个参数就叫做泛型
泛型的作用:
- 解决元素存储的安全性问题。好比药品标签,不会放错
- 解决获取元素时,需要类型强制转换的问题。好比不用每回拿药品都要辨别是不是需要的药品
泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生
ClassCastException
异常
同时代码更加简洁健壮
代码示例:
ArrayList<Integer> list = new ArrayList<>();//类型推断
list.add(12);
list.add(34);
list.add(56);
//list.add("hello");添加失败
//遍历方式一:
for(Integer i : list){
//不需要强转
System.out.println(i);
}
//遍历方式二:
Iterator<Integer> iterator = list.iterator();
while(iterator.hashNext()){
System.out.println(iterator.next());
}
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom", 12);
map.put("Jack", 13);
map.put("Marry", 14);
map.put("Jane", 15);
//map.put(16, "Mike");添加失败
//泛型的嵌套
Set<Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Entry<String, Integer>> iterator = entrySet.iterator();
while(iterator.hashNext()){
Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
//JDK 1.5之前
Comparable c = new Date();
System.out.println(c.compareTo("red"));//运行时报错
//JDK 1.5
Comparable<Date> c = new Date();
System.out.println(c.compareTo("red");//编译时报错
- 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内,比如<E1, E2, E3>
- 集合接口或集合类在JDK 5.0时都修改为带泛型的结构,在实例化集合类时,可以指明具体的泛型类型。指明后,在集合类或接口中,凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型
- 泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的,转换为包装类
- 如果实例化时没有知名泛型的类型,默认类型为Object类型
- 泛型不同的引用不能相互赋值
尽管在编译时ArrayLisy<String>和ArrayList<Integer>是两种类型,但是在运行时只有一个ArrayList被加载到JVM中
- 异常类不能有泛型
- 如果要造泛型数组:
T[] arr = new T[10];//编译不通过
T[] arr = (T[]) new Object[10];//编译通过。但其实造的还是一个Object数组,只不过你用它来存放T类型元素
ArrayList<String> list1 = null;
ArrayList<Integer> list2 = null;
list1 = list2;//在泛型中,这是不被允许的
- 静态方法中不能使用类的泛型(泛型代表着不确定,静态代表着确定。静态中当然不能有不确定);try-catch中不能使用泛型
自定义泛型结构
自定义泛型类、接口
public class Order<T>{
String orderName;
T orderT;//T类型,名称叫orderT
public Order(){};
public Order(String orderName, T orderT){
this.orderName = orderName;
this.orderT = orderT;
}
}
public class Test{
Order order1 = new Order("Tom", 12);
Order order2 = new Order("Tom", "Jack");
//以上两个都是可以运行通过的。因为虽然定义了泛型,但是没有指明泛型,所以默认泛型是Object类
Order<String> order3 = new Order<>("Tom", 12);//指明了泛型,此时非法,编译报错
Order<String> order4 = new Order<>("Tom", "Jack");//编译运行通过
}
继承中的泛型:
- 子类在继承带泛型的父类时,父类指明了泛型类型,则实例化子类对象时,不需要指明泛型。此时子类不再是泛型类
public class SubOrder extends Order<Integer>{};
public class Test{
SubOrder sub = new SubOrder("Tom", 12);
}
- 子类在继承带泛型的父类时,父类没有指明泛型类型,此时子类仍是泛型类,使用时可以自行指明泛型类型
public class SubOrder<T> extends Order<T>{};
public class Test{
SubOrder<String> sub = new SubOrder<>("Tom", "Jack");
}
- 父类有多个泛型
- 子父类关系无法在泛型中得到继承
A是B的父类,G<A>不是G<B>的父类,二者不具备子父类关系,是并列关系
但A<G>是B<G>的父类
自定义泛型方法
public class Order<T>{
String orderName;
T orderT;//T类型,名称叫orderT
public Order(){};
public Order(String orderName, T orderT){
this.orderName = orderName;
this.orderT = orderT;
}
public T getOrderT(){
return orderT;
}
//以上这些出现T的方法,都不是泛型方法。因为这个T在类实例化时会传入,这些方法中的T实际上是确定的,就是和传入的T相同
//以下这种,在方法中出现了泛型的结构,泛型参数与类没有任何关系的方法,是泛型方法
//换句话说,泛型方法只与方法本身有关,与所属的类是不是泛型类都没有关系
//非泛型类也可以有泛型方法;泛型类即使有泛型方法,其泛型参数T和泛型方法的泛型参数E,相互独立,没有关系
public <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
//泛型方法可以是静态的
public static <E> List<E> copyFromArrayToList(E[] arr){};
//前面泛型类为什么说静态方法中不能使用类的泛型?因为静态方法在实例化类之前加载,没有实例化类也可以使用静态方法。然而此时类的泛型参数还没有确定,所以静态方法不能使用不确定的东西
//这里的静态方法为什么可以带泛型参数?因为这个泛型参数是方法本身的,要调用这个方法,就必然要传入泛型参数,否则无法调用。换句话说,调用静态方法时,泛型参数是确定的,所以这个泛型参数,对静态方法来说,是确定的,即使不实例化,也可以调用静态方法
}
通配符
类型通配符:?
比如:List<?>,Map<?, ?>
List<?>是List<String>、List<Object>等各种泛型List的父类
- 读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object
- 不能向list中写入元素,因为我们不知道其中的元素类型。将任意元素加入到list中都是不安全的。唯一例外的是null,因为它是所有类型的成员
Collection<?> c = new ArrayList<String>();
c.add(new Object());//编译报错
注意点:
- 通配符不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?>list){} //报错
- 不能用在泛型类的声明上
class test<?>{} //报错
- 不能用在创建对象上,右边属于创建集合对象
ArrayList<?> list = new ArrayList<?>(); //报错