在日常的開發中,我們會看到别人的架構很多地方會使用到泛型,泛型是java se 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的資料類型被指定為一個參數。這種參數類型可以用在類、接口和方法的建立中,分别稱為泛型類、泛型接口、泛型方法。泛型的類型參數隻能是類類型(包括自定義類),不能是簡單類型。本篇部落格我們就來詳細解析一下泛型的知識。
泛型類定義及使用
使用泛型有什麼好處呢?首先我們先看一個例子,假設我們有兩個類,代碼如下:
#stringclass
public class stringclass {
private string x ;
private string y ;
public string gety() {
return y;
}
public void sety(string y) {
this.y = y;
public string getx() {
return x;
public void setx(string x) {
this.x = x;
}
#intclass
public class intclass {
private int x ;
private int y ;
public int gety() {
public void sety(int y) {
public int getx() {
public void setx(int x) {
觀察上面兩個類stringclass 和intclass,他們除了變量類型不一樣,一個是string一個是int以外,其它并沒有什麼差別!那我們能不能合并成一個呢?通過泛型就可以解決,首先看一下泛型的類是怎麼定義的:
public class objclass<t> {
private t x ;
private t y ;
public t getx() {
public void setx(t x) {
public t gety() {
public void sety(t y) {
那麼這時候上面的兩個類就可以通過泛型的設定,相應生成
objclass<string> stringclass = new objclass<string>();
stringclass.setx("haha");
objclass<integer> intclass = new objclass<integer>();
intclass.setx(100);
log.d("yyy", "stringclass:" + stringclass.getx() + ",intclass:" + intclass.getx());
從結果中可以看到,我們通過泛型實作了開篇中stringclass類和intclass類的效果。
接下來介紹泛型如何定義及使用:
1.首先需要定義泛型:objclass
objclass ,即在類名後面加一個尖括号,括号裡是一個大寫字母。這裡寫的是t,其實這個字母可以是任何大寫字母,無論使用哪個字母,意義都是相同的。如果你想學習java可以來這個群,首先是二二零,中間是一四二,最後是九零六,裡面有大量的學習資料可以下載下傳
2.在類中使用泛型
這個t表示派生自object類的任何類,比如string,integer,double等等。這裡要注意的是,t一定是派生于object類的。
private t x ;
3.使用泛型類
泛型類的使用代碼如下:
首先,需要構造一個執行個體:
泛型類的構造則需要在類名後添加上,即一對尖括号,中間寫上要傳入的類型。
因為我們構造時,是這樣的:objclass,是以在使用的時候也要在objclass後加上類型來定義t代表的意義。
尖括号中,你傳進去的是什麼,t就代表什麼類型。這就是泛型的最大作用,我們隻需要考慮邏輯實作,就能拿給各種類來用。
多泛型變量定義
1.多泛型變量定義
我們不止可以在類中設定一個泛型變量t,還可以聲明多個泛型變量,寫法如下:
public class objclass<t,u>
也就是在原來的t後面用逗号隔開,寫上其它的任意大寫字母即可,如果還有多個,依然使用逗号分隔開即可,則我們前面定義的泛型類就會變成下面這樣:
public class objclass<t,u> {
private u y ;
public u gety() {
public void sety(u y) {
objclass<string,integer> stringclass = new objclass<string,integer>();
stringclass.sety(100);
從上面的代碼中,可以明顯看出,就是在新添加的泛型變量u用法與t是一樣的。
2.泛型的字母規範
雖然在類中聲明泛型任意字母都可以,但為了可讀性,最好遵循以下的規範:
e — element,常用在java collection裡,如: list<e>,iterator<e>,set<e>
k,v — key,value,代表map的鍵值對
n — number,數字
t — type,類型,如string,integer等等
泛型接口定義及使用
在接口上定義泛型與在類中定義泛型是一樣的,代碼如下:
interface msgclass<t> {
public t getmsg() ;
public void setmsg(t x);
我們可以利用泛型類來構造填充泛型接口
public class message<t,u> implements msgclass<t>{
private t msg;
@override
public t getmsg() {
return msg;
public void setmsg(t msg) {
this.msg = msg;
在這個類中,我們構造了一個泛型類message,然後把泛型變量t傳給了msgclass,這說明接口和泛型類使用的都是同一個泛型變量。
我們還可以構造一個多個泛型變量的類,并繼承自msgclass接口:
private u name;
public u getname() {
return name;
public void setname(u name) {
this.name = name;
泛型函數定義及使用
我們不但可以在類聲明中使用泛型,還可以在函數聲明中也使用泛型,使用如下:
public class objclass {
//靜态函數
public static <t> void staticmethod(t a) {
//普通函數
public <t> void orgnicmethod(t a) {
上面分别是靜态泛型函數和正常泛型函數的定義方法,與以往方法的唯一不同點就是在傳回值前加上來表示泛型變量。
無論哪種泛型方法都有兩種使用方法:
//靜态方法
objclass.staticmethod("adfdsa");//使用方法一
objclass.<string>staticmethod("adfdsa");//使用方法二
//正常方法
objclass objclass = new objclass();
objclass.orgnicmethod(new integer(111));//使用方法一
objclass.<integer>orgnicmethod(new integer(111));//使用方法二
方法一,隐式傳遞了t的類型,這種隐式的傳遞方式,代碼不利于閱讀和維護。因為從外觀根本看不出來你調用的是一個泛型函數。
方法二,例如上面例子中,将t指派為integer類型,這樣orgnicmethod(t a)傳遞過來的參數如果不是integer那麼編譯器就會報錯。
當然泛型函數的傳回值也可以使用泛型表示:
public static <t> list<t> parsearray(string response,class<t> object){
list<t> modellist = json.parsearray(response, object);
return modellist;
函數傳回值是list類型。和void的泛型函數不同,有傳回值的泛型函數要在函數定義的中在傳回值前加上辨別泛型;還要說明的是,上面中,使用class傳遞泛型類class對象
泛型數組
泛型同樣可以用來定義在數組上
//定義
public static <t> t[] fun1(t...msg){ // 接收可變參數
return msg ; // 傳回泛型數組
}
//使用
public static void main(string args[]){
integer i[] = fun1(8,9,8,44) ;
integer[] result = fun1(i) ;
定義了一個靜态函數,然後定義傳回值為t[],參數為接收的t類型的可變長參數。
泛型的通配符
在開發中對象的引用傳遞(向上向下傳遞)是最常見的,但是,在泛型的操作中,在進行引用傳遞的時候泛型類型必須比對才可以傳遞,否則不能傳遞。
例如,如下沒有進行泛型類型比對,一個是string,一個是object類型。
class info<t>{
private t var ; // 定義泛型變量
public void setvar(t var){
this.var = var ;
public t getvar(){
return this.var ;
public string tostring(){
return this.var.tostring() ;
};
public class demo1 {
public static void main(string args[]) {
// 使用string為泛型類型
info<string> i = new info<string>();
i.setvar("abcd");
//把string泛型類型的i對象傳遞給object泛型類型的temp。
fun(i);
// 接收object泛型類型的info對象
public static void fun(info<object> temp) {
system.out.println("内容:" + temp);
編譯發生錯誤。
exception in thread "main" java.lang.error: unresolved compilation problem:
the method fun(info<object>) in the type demo1 is not applicable for the arguments (info<string>)
at thread1.demo1.main(demo1.java:18)
泛型對象進行引用傳遞的時候,類型必須一緻,如果非要傳遞,則可以将fun方法中info參數的泛型取消掉(變成 void fun(info temp))。、
以上确實改進了功能,但是似乎不是很妥當,畢竟之前指定過泛型。
以上程式在fun()方法中使用 "info<?>" 的代碼形式,表示可以使用任意的泛型類型對象,這樣的話fun()方法定義就合理了,但是使用以上方法也有需要注意的地方,
即:如果使用“?“接收泛型對象的時候,則不能設定被泛型指定的内容。
private t var ;
public string tostring(){
public class genericsdemo{
public static void main(string args[]){
info<string> i = new info<string>() ;
i.setvar("abcd") ;
fun(i) ;
public static void fun(info<?> temp){
system.out.println("内容:" + temp) ;
如果使用”?“意味着可以接收任意的内容,但是此内容無法直接使得用”?“修飾的泛型的對象進行修改。如下就會出問題:
public class demo1{
info<?> i = new info<string>() ;
運作結果:
the method setvar(capture#1-of ?) in the type info<capture#1-of ?> is not applicable for the arguments (string)
at thread1.demo1.main(demo1.java:17)
在使用”?“隻能接收,不能修改。
泛型的上限
info<integer> i1 = new info<integer>() ;
info<float> i2 = new info<float>() ;
i1.setvar(30) ;
i2.setvar(30.1f) ;
fun(i1) ;
fun(i2) ;
public static void fun(info<? extends number> temp){ // 隻能接收number及其number的子類
system.out.print(temp + "、") ;
運作成功。但是,如果傳入的泛型類型為string的話就不行,因為string不是number子類。
在類中使用泛型上限。
class info<t extends number>{ // 此處泛型隻能是數字類型
info<integer> i1 = new info<integer>() ; // 聲明integer的泛型對象
如果在使用info的時候設定成string類型,則編譯的時候将會出現錯誤(string不是number子類)
注意:利用 <? extends number> 定義的變量,隻可取其中的值,不可修改
原因如下:
因為info的類型為 info
泛型的下限
<? super xxx> 表示填充為任意xxx的父類
public class genericsdemo21{
info<string> i1 = new info<string>() ; //
info<object> i2 = new info<object>() ; //
i1.setvar("hello") ;
i2.setvar(new object()) ;
public static void fun(info<? super string> temp){ // 隻能接收string或object類型的泛型,string類的父類隻有object類
object類和string類都是string的父類,所有運作成功,但是如果此時用integer則會出錯,因為integer并不是string父類。
注意:使用super通配符:能存不能取
如何了解呢?假設有3個類,繼承關系如下:
class ceo extends manager {
}
class manager extends employee {
class employee {
然後書寫如下代碼:
list<? super manager> list;
list = new arraylist<employee>();
//存
list.add(new employee()); //編譯錯誤
list.add(new manager());
list.add(new ceo());
為什麼而list.add(new employee());是錯誤的?
因為list裡item的類型是
list<employee> list = new arraylist<employee>();
在這裡,正因為manager和ceo都是employee的子類,在傳進去list.add()後,會被強制轉換為employee!
現在回過頭來看這個:
編譯器無法确定 <? super manager> 的具體類型,但唯一可以确定的是manager()、ceo()肯定是 <? super manager> 的子類,是以肯定是可以add進去的。但employee不一定是 <? super manager> 的子類,是以不能确定,不能确定的,肯定是不允許的,是以會報編譯錯誤。
最後強調一下, list<? super manager> list取出的隻能是object 類型,這裡雖然看起來是能取的,但取出來一個object類型,是毫無意義的。是以才有了“super通配符:能存不能取”的結論。
總結1)使用?可以接收任意泛型對象。
2)泛型的上限:?extends 類型(能取不能存)。
3)泛型的下限:?super 類型? super 通配符(能存不能取)。