一、泛型的出現
java泛型,是JDK1.5之後加入的功能。
二、泛型的作用
我們都知道,類是一個模板,可以用來實例化對象。如果一個類沒有使用泛型,那麼它就是一個“專用”模板,可以產生一種對象;而如果類中使用了泛型,那它就是一個“通用”模板,可以產生多種對象。
專用模板就好像下面這些:
- 橫縱座標的數值爲 int 類型的座標類。
/**
* 座標類:橫縱座標的數值爲 int類型
*/
class Point{
private int x;
private int y;
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
public int getX(){
return this.x;
}
public int getY(){
return this.y;
}
}
- 橫縱座標的數值爲 double 類型的座標類
/**
* 座標類:橫縱座標的數值爲 double類型
*/
class Point{
private double x;
private double y;
public void setX(double x){
this.x = x;
}
public void setY(double y){
this.y = y;
}
public double getX(){
return this.x;
}
public double getY(){
return this.y;
}
}
橫縱座標的數值爲 String 類型的座標類
/**
* 座標類:橫縱座標的數值爲 String類型
*/
class Point{
private String x;
private String y;
public void setX(String x){
this.x = x;
}
public void setY(String y){
this.y = y;
}
public String getX(){
return this.x;
}
public String getY(){
return this.y;
}
}
這裏的座標類,我們發現它們除了數據類型不同,類的結構和功能完全一樣。我們嘗試用一個統一的符號Type簡寫爲T替換掉其中的數據類型如int、double、String。我們在類名稱後面用<T>表示我們使用了T來代替具體的數據類型。
於是我們得到了:
class Point<T>{
private T x;
private T y;
public void setX(T x){
this.x = x;
}
public void setY(T y){
this.y = y;
}
public T getX(){
return this.x;
}
public T getY(){
return this.y;
}
}
三、泛型類的實例化
可是我們如何指定我們要用的類型是什麼呢?
我們需要在實例化的時候指定這個實例到底是什麼樣的對象。於是我們約定,實例化的時候在類名稱後面以<>註明需要的數據類型,也就是int、double、String等或者是一些自定義的類,在這裏,由於int、double、char等基本數據類型不是Object的子類,換言之,這些基本類型的數據並不是對象。因此它們需要進行包裝之後放入<>,即<>中如果使用基本數據類型,需要使用其對應的包裝類。
例如:指定橫縱座標數值類型爲 Integer的 Point對象。
Point<Integer> point = new Point<Integer>();
例如:指定橫縱座標數值類型爲 Double 的 Point對象。
Point<Double> point = new Point<Double>();
例如:指定橫縱座標數值類型爲 String 的 Point對象。
Point<String> point = new Point<String>();
這樣,創建不同類型的對象,不再需要創建那麼多的類了,只需要創建一個泛型類,並在實例化的時候指明需要的數據類型即可。
看到這裏你會說爲什麼不用Object呢?
可以的,只是使用Object來定義類中的數據類型,該類在使用時必定會進行向下轉型,向下轉型是高風險操作。因爲轉型帶來的問題不會在編譯時發現,可能會在程序運行時帶來“ClassCastException” 。因此,使用Object類定義類中的數據類型是不推薦的。
四、通配符與引用傳遞
在使用該對象時,我們創建一個方法處理該對象。在方法聲明中,需要指明該對象是一個泛型對象,即:該對象所屬的類定義中使用了泛型。在引用時由於不知道泛型中具體的類型會是什麼,所以用?代替:<?>,範例如下。
public static void fun(Point<?> temp){
System.out.println("x = " + temp.getX());
System.out.println("y = " + temp.getY());
}
在使用泛型爲形式參數的方法中,爲了保證盡肯能的安全,只允許數據的獲取,不允許數據的修改。(類型的不知道蝦改個啥)
在“?”這個通配符的基礎之上,實際上還提供了兩類小的通配符:
1.?extends類:設置泛型的上限
|- 例如:定義 “?extends Number”:表示該泛型類型只允許設置Number或Number的子類;
public static void fun(Point<? extends Number> temp){
System.out.println("x = " + temp.getX());
System.out.println("y = " + temp.getY());
}
2.?super類:設置泛型的下限
|- 例如:定義“?superString” :只能夠使用String或其父類;
public static void fun(Point<? super String> temp){
System.out.println("x = " + temp.getX());
System.out.println("y = " + temp.getY());
}
一些系統類庫中大量的使用了通配符。
五、泛型與接口
泛型除了可以在類上定義之外也可以直接在接口之中進行使用。例如:下面定義個泛型接口:
範例:定義一個泛型接口
interface IMessage<T> {
public String echo(T t);//表示消息的迴應
}
對於泛型接口的子類而言現在就有兩種實現方式。
實現方式一:在子類之中繼續設置泛型定義。
interface IMessage<T> {
public String echo(T t);//表示消息的迴應
}
class MessageImpl<S> implements IMessage<S>{
public String echo(S t){
return "【ECHO】 " + t;
}
}
public class PointDemo {
public static void main(String[] args) {
IMessage<String> msg = new MessageImpl<>();
System.out.println(msg.echo("www.cell.com"));
}
}
實現方式二:在子類實現父接口的時候,直接定義出具體泛型類型
範例:
interface IMessage<T> {
public String echo(T t);//表示消息的迴應
}
class MessageImpl implements IMessage<String>{
public String echo(String t){
return "【ECHO】 " + t;
}
}
public class PointDemo {
public static void main(String[] args) {
IMessage<String> msg = new MessageImpl();
System.out.println(msg.echo("www.cell.com"));
}
}
六、泛型方法
在之前的程序類裏面實際上已經可以發現在泛型類之中如果將泛型標記使用在了方法上,那麼這樣的方法就被稱爲是泛型方法。但是需要注意的是,泛型方法不一定非要出現在泛型類之中。即:如果一個類上沒有定義泛型,那麼也可以使用泛型方法。
範例:
public class PointDemo {
public static void main(String[] args) {
Integer[] num = fun(1,2,3);//傳入了整數,泛型類型就是Integer
for(int i:num){
System.out.println(i);
}
}
public static <T> T[] fun(T ... args){
return args;
}
}
七、小結
泛型的作用是開發出可重用性更高且安全的程序,因此被廣泛使用在各種流行框架的源碼和JDK源碼之中。