近来重温了一下《Core Java》中的第8章,关于泛型的描述,重拾了一下之前的知识和理解,总结几点思考和感悟如下:
-
泛型的声明
在类或者方法的定义过程中,可以进行泛型的声明(一般用大写字母T来表示),以便类或者方法的后续编写过程中可以使用这个T作为特定类的表示。
-
泛型在类定义时的声明
public class Pair<T> {
private T first;
private T second;
public T getFirst(){}
public T getSecond(){}
public void setFirst(T first){
this.first = first;
}
public void setSecond(T second){
this.second = second;
}
}
跟在Pair后在尖括号中的T就是类定义中泛型的声明。
-
泛型在方法定义时的声明
类似的,方法定义过程中也可以进行泛型的声明,一般是在方法的modifiers后返回值前。
public class MethodGeneric{
public static <T> T printAndReturn(T t){
System.out.println("do nothing then return input parameter.");
return t;
}
}
值得注意的有2点:
注意点1,泛型类型可以继承1个类并同时实现若干个接口,均用关键字extends来表示,继承的父类(仅能继承1个)必须放在extends后第一位,实现多个接口用&连接起来,如下代码所示:
public class GenericExtends<T extends BaseClass & Comparable & Serializable> {
//class body
}
注意点2,泛型T是一种编译时的概念,而不是运行时的概念。也就是说在Java虚拟机运行时是没有泛型的概念的,统一为Object类或者泛型T所继承的类或者实现的接口(一般替换为声明时extends后的第一个)。编译时会在必要的return等地方根据使用泛型时传入的具体类型做强制类型转换。
-
泛型的上界和下界
说到泛型的上界和下界,必须提到通配符(wildcard——”?“)。为了方便说明,我们定义两个类:父类Father和子类Son,以及泛型类Pair。
public class Pair<T> {
private T first;
private T second;
public T getFirst(){}
public T getSecond(){}
public void setFirst(T first){
this.first = first;
}
public void setSecond(T second){
this.second = second;
}
}
class Father{}
class Son extends Father{}
首先是泛型的上界,例如? extends Father,这里需要特别注意的是此处是使用泛型时这个具体传给T的类型必须是Father的子类。使用上界时可以用get方法,而无法使用set方法,这个具体该怎么理解呢,我们看一下的案例
public static void main(String[] args){
Pair<? extends Father> p = new Pair<>();
p.getFirst(); //OK
Son s = p.getFirst(); //Error,T为Fahter的子类,因此getFirst()可能回传Father或者Son,因此不能用Son来接收,只能用Father来接收。
Father f = p.getFirst(); //OK
p.setFiert(new Father());
//Error,因为此时Pair中的类型T是必须是Father的子类,但是并不知道具体是哪一个子类,究竟是Father还是Son,不明确,所以无法传具体的类型给T。
}