泛型(一)->简单使用
从上周末到现在陆陆续续看了几天终于把<< java核心技术 >>泛型看完了,有种豁然开朗的感觉尤其是对于泛型擦除又有了新的认识,趁脑还热赶紧记录下来.
关于泛型我准备分两篇写,第一篇是关于泛型的使用属于基础(必须掌握),第二篇是泛型擦除等等一些问题属于进阶(可选).大家自行选择.
首先我们要知道泛型的英文是Generic,我曾经被中文版的安卓文档坑过他给翻译成一般结果始终不理解.
泛型类的定义
泛型类就是具有一个或者多个类型变量的类.
public class Pair<T> {
private T first;
private T second;
public Pair() {
first = null;
second = null;
}
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
注意:本篇博客栗子都会用到这个类,后面使用的Pair就是指的这个类.
Pair引入了类型变量T,用<>括起来,放在类名后.泛型类可以有多个类型变量例如
public class Pair<T,U>{...}
类中指定的类型变量可以用来指定方法的返回值类型,成员变量,局部变量的类型.例如
public T getFirst() {...}
用具体的类型替换类型变量就可以实例化泛型类,例如
Pair<String> pair = new Pair<>();
这里举个实际的栗子,用静态的minmax方法遍历数组并计算最大最小值,用一个Pair对象返回两个结果
public class ArrayAlg {
public static Pair<String> minmax(String[] a) {
if (a == null || a.length == 0)
return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++) {
if (min.compareTo(a[i]) > 0)
min = a[i];
if (max.compareTo(a[i]) < 0)
max = a[i];
}
return new Pair<String>(min, max);
}
}
public static void main(String[] args){
String[] words = {"crash","have","bad","a"};
Pair<String> pair = ArrayAlg.minmax(words);
System.out.println("min:"+pair.getFirst());
System.out.println("max:"+pair.getSecond());
}
泛型方法
前面已经介绍了泛型类. 其实还可以定义一个带有类型参数的简单方法
public class ArrayAlg {
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
}
这是一个泛型方法,从<>括号可以看出这一点,需要注意的是泛型方法类型变量放在修饰符后面(这里是public static)返回值的前面.泛型方法可以定义在普通类,也可以定义在泛型类中.
调用一个泛型方法也非常简单
String middle = ArrayAlg.getMiddle("zly","ppjun","public");
编译器能够根据传入的String[]判断出T一定是String.
类型变量的限定
有的时候我们需要对类型变量加以限定,比如我们想利用Compareble接口的compareTo方法去比较大小.那么我们就需要传入的参数实现了Compareble接口.这个时候就可以通过类型变量T设置限定符实现
public static <T extends Compareble> T min(T[] a)
现在,泛型的min方法就只能被实现了Compareble接口的类的数组调用
一个类型变量或者通配符可以有多个限定,例如
T extends Compareble & Serializeble
限定符用&分隔,类型变量用,分隔
由于java是单继承多实现的,所以限定中可以有多个接口,但是最多只有一个类,并且如果用一个类做限定那么他必须放限定列表中第一个.
需要注意的是类型变量的限定只支持< T extends X >形式,不支持< T super X >形式不要跟后面介绍的通配符”?”混淆
泛型类型的继承规则
在使用泛型的时候我们需要了解一些关于子类和继承的规定,先举一个例子Employee和Manager是继承关系,而Pair< Employee >是Pair< Manager >父类么?答案是”不是”.相信这和大多数人的想法不太一样.
比如下面这个情况我们写代码时候应该都遇到过
public static void main(String[] args){
List<Manager> list = new ArrayList<>();
test(list);
}
public static void test(List<Employee> list){
}
test方法没法调用,简单的说无论T和S有什么关系Pair< T >与Pair< S >没有任何联系
泛型类可以扩展或者实现其他泛型类,这跟普通类没有什么区别,比如ArrayList< T >实现List< T >,一个ArrayList< Manager >可以被转换为List< Manager >,但是一个ArrayList< Manager >跟ArrayList< Employee >没有关系
通配符类型
通配符的子类限定
Pair< ? extends Employee >
表示类型参数是Employee子类的Pair
像前面说的给test方法无法传入List< Manager >的问题,解决方法很简单:使用通配符
public static void test(List<? extends Employee> list)
List< Manager >是List< ? extends Employee >的子类
这里有个问题需要注意当使用了List< ? extends Employee >后,list.add()方法将不能使用而list.get()是可以的,要了解原因看传入< ? extends Employee >后的add和get方法.
boolean add(? extends Employee e)
? extends Employee get(int index)
编译器只知道add方法参数是Employee的子类,但并不知道具体是什么类型.add方法拒绝传入任何特定的类型因为?不能跟他匹配.
而get方法就不存在问题,我们将返回值赋给Employee完全合法.
通配符的超类限定
除了与类型变量十分相似extends限定,通配符还有一个附加能力,即指定一个超类限定符,如下
? super Manager
这个通配符限制为Manager或者所有父类.可以为方法提供参数,但不能使用返回值.例如List< ? super Manager >
boolean add(? super Manager e)
? super Manager get(int index)
编译器不知道add方法确切类型,但是可以用Manager(或其子类)调用它,然而get方法因为返回值不确定就只能用Object去接收他
总结下,带有超类型限定(super)的通配符可以向泛型对象写入,带有子类型限定(extends)的通配符可以从泛型对象读取
无限定通配符
还可以使用无限定的通配符,例如Pair< ? >,并且会有如下方法
? getFirst()
void setFirst(?)
getFirst方法返回值只能赋给object,而setFirst方法将不能被调用即使传入Object也不行.那么问题来了这个有什么卵用.
看完下面这个栗子就知道啦
public static boolean isNull(Pair<?> pair){
return pair.getFirst() != null || pair.getSecond() != null;
}
没错就是这种判断是否为空的操作用?非常好使,因为他不需要知道实际的类型.
总结
泛型类定义
public class Pair<T> {...}
泛型方法定义
public static <T> T getMiddle(T... a) {...}
泛型变量限定
<T extends X>
代表T只能为X或者X的子类,不支持<T super X>
一个类型变量或者通配符可以有多个限定,
T extends Compareble & Serializeble
,但是最多只有一个类,并且如果用一个类做限定那么他必须放限定列表中第一个.
泛型类的继承规则
- 无论T和S有什么关系Pair< T >与Pair< S >没有任何联系
通配符的子类限定
Pair<? extends Employee>
表示类型参数是Employee子类的Pair使用了List< ? extends Employee >后,list.add()方法将不能使用而list.get()是可以的.
通配符的超类限定
Pair<? super Manager>
这个通配符限制为Manager或者所有父类,可以为方法提供参数,但不能使用返回值.
预告
本篇到此结束,下一篇泛型(2)将讲解泛型擦除和擦除所带来的形形色色的问题敬请期待.