泛型
泛型,即参数化类型
,类似于方法中将变量
参数化,泛型是将原来定义的具体的类型
参数化。
使用泛型的需求
Java中为什么要使用泛型,是因为泛型使用起来非常之方便,泛型类/方法针对于面向复用的开发。当我们想让一个类/方法同时适配于多种数据类型,这将大大省去我们编写重复代码的时间。
举一个常见的例子说明:
List<Integer> ilist = new ArrayList<Integer>();
List<String> slist = new ArrayList<String>();
其中List接口以及类ArrayList均使用了泛型,从而可以构造出接收以类型Integer, String为参数的list;
所以下面的add操作中可以用不同类型的变量作为参数(用泛型可以省去重载方法的开销):
ilist.add(1);
ilist.add(2);
System.out.println(ilist);
slist.add("a");
slist.add("b");
System.out.println(slist);
分别输出为[1, 2] [a, b]
泛型应用场景
可以看到,除了部分底层的实现类,上层的类/抽象类/接口基本都是用泛型实现的,用泛型实现能够很好地进行复用,比如这里的L可以是Employee,Course,Process,从而派生出三个具体子类DutyIntervalSet,CourseIntervalSet,ProcessIntervalSet。
下面我们将以上图为例介绍如何编写一个自己的泛型类。
如何编写自己的泛型类
我们以MultiIntervalSet为例进行分析:
编写面向泛型的类时需要注意以下几点:
①泛型类的声明:需要在类名后加上<L>
public class MultiIntervalSet<L> implements IMultiIntervalSet<L>
②需要用到泛型的地方,全部用L
代替
protected final Set<L> elabel = new HashSet<>();
protected final Map<L, List<Interval>> emap = new HashMap<>();
③只能对定义为泛型的变量做一些通用的操作
:
对于一个L label的泛型变量label,可以对它进行list.add(label),set.contains(label)等操作,由于它在具体传入的时候已经变成了一个具体的类。但是想要显式地调用它自己的方法只能调用如下几种(此时感觉label就是一个原始的Object类):
要小心,不要代入某个具体的类型,调用它特有的方法;
所以这也是泛型的一个局限性,对于通用的操作可以复用,但如果要用到某个具体类的方法,则不能将该类型作为参数传递。
④泛型类中可以有非泛型方法,也可以有非泛型的成员变量。
这点很好理解,泛型类中不一定每个方法都需要用到泛型,用到泛型时才是泛型方法;同时泛型类中当然也可以有具体类型的成员变量。
protected final Set<L> elabel = new HashSet<>();
protected final Map<L, List<Interval>> emap = new HashMap<>();
protected Interval duration;
最后给出一个泛型方法的例子
public boolean remove(L label) {
if(!elabel.contains(label)) return false;
else {
emap.remove(label);
elabel.remove(label);
checkRep();
return true;
}
}
总结
总之,写泛型类的时候要保持平常心,将L
当作一个普通的类型去编写代码,开始时不要因为觉得L
的写法陌生,而不敢对它进行操作,或者不愿意自定义泛型类/方法。
补充介绍:类型通配符
类型通配符一般是使用?
代替具体的类型参数。例如 List<?>
在逻辑上是List<String>
,List<Integer>
等所有List<具体类型实参>的父类。
①在某些情况下,我们必须使用到类型通配符,否则静态编译时会报错:
比如:重写equals函数时用到instanceof
运算符
@Override public boolean equals(Object obj) {
if(obj instanceof Edge<L>) {
Edge<L> e = (Edge<L>)obj;
//...后续逻辑省略
编译报错:Cannot perform instanceof check against parameterized type Edge. Use the form Edge<?> instead since further generic type information will be erased at runtime
修改为下图所示,编译通过。
if(obj instanceof Edge<?>) {
Edge<?> e = (Edge<?>)obj;
②类型通配符还可以用于特定的情形
比如限定类型的上/下限:
List<? extends Number>
限定接收类型的上限为Number,接受Number及其下层子类类型List<? super Number>
限定接收类型的下限为Number,接受Number及其三层父类类型,如 Object 类型的实例。