泛型的基本使用
泛型是java se 1.5的新特性, 泛型的本質是參數化類型, 也就是說所操作的數據類型被指定為一個參數. 這種參數類型可以用在類、接口和方法的創建中, 分別稱為泛型類、泛型接口、泛型方法. java語言引入泛型的好處是安全簡單.
今天就從以下幾個方面介紹一下java的泛型: 基礎, 泛型關鍵字, 泛型方法, 泛型類和接口.
基礎:
通過集合的泛型了解泛型的基本使用
1
2
3
4
5
6
|
public void testbasis(){ list<string> list = new arraylist<string>(); // new arraylist<int>(); } //這是最基本的泛型使用, 就不多說了, 不過要注意的是泛型只能是引用數據類型, 不能是基本類型, 而且泛型只在編譯期有效, 在編譯后的class文件中是不存在泛型信息的 |
注意: 泛型只在編譯期有效, 在編譯后的class文件中是不存在泛型信息的
泛型關鍵字:
通配符?表示任意引用類型, extends關鍵字表示子類及其本身, super關鍵字是表示父類及其本身. 通過一個例子看一下, 解釋說明都在例子中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public void testkeyword() throws exception { //實例化參數類型必須指明具體類型 list<?> list = new arraylist<string>(); //由于list中類型不明確, 所以不能進行添加操作 // list.add("sk"); //通配符?表示任意引用類型 list<list<?>> list1 = new arraylist<list<?>>(); //list1的參數化類型是一個list, 而這個list也是一個參數化類型, 它的類型是任意類型, 所以list1可以添加任意list類型對象 list1.add( new arraylist<object>()); list1.add( new arraylist<string>()); //實例化list2時指明了類型參數list, 只不過這個list是一個泛型類型 //從這里可以看到關鍵字extends的用法, 其實就是繼承, 如下這里的list2中的參數化類型list(在這里記為paramlist)的參數化類型是繼承自number的 //所以在list2在添加的時候只能添加參數化類型為number及其子類的paramlist list<integer> l1 = new arraylist<integer>(); list<number> l2 = new arraylist<number>(); list<object> l3 = new arraylist<object>(); list<list<? extends number>> list2 = new arraylist<list<? extends number>>(); list2.add(l1); list2.add(l2); // list2.add(l3); //這里使用了extends關鍵字, 所以不能添加number的父類 //相信大家也猜到了, 既然extends關鍵字表示子類及其本身, 那么super關鍵字是表示父類及其本身, 是的, 沒錯 //和上面的不一樣了, l1不能添加, 因為integer是number的子類, 并不是number的父類 list<list<? super number>> list3 = new arraylist<list<? super number>>(); // list3.add(l1); //這里使用了super關鍵字, 所以不能添加number的子類 list3.add(l2); list3.add(l3); } |
泛型方法:
java中任何類型必須先定義才能使用, 泛型也是如此. 既然要使用泛型作為參數, 所以要先定義, 泛型的定義在訪問修飾符和返回類型之間, 注意不要掉了尖括號
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
//無返回值有參的方法, 參數為泛型 public <t> void show(t t){ system.out.println( "t-=-=" + t); } //有返回值的有參方法, 只有一個參數化類型, 這里定義泛型的方式和上面一樣, 也是先定義后使用, 只不過這里的返回類型是泛型 public <t> t get(t t){ return t; } //有返回值的有參方法, 有多個參數化類型, 這里以兩個為例 public <t, k> k get(t t, k k){ return k; } @test public void testgeneric() throws exception { show( 3 ); show( "generic" ); system.out.println( "----------------" ); system.out.println( "我返回integer類型-" + get( 4 )); system.out.println( "我返回string類型-" + get( "returngeneric" )); system.out.println( "------------------" ); system.out.println( "我返回string類型-" + get( 1 , "a" )); system.out.println( "我返回integer類型-" + get( "b" , 2 )); } |
本來想寫無參的泛型方法, 可是寫著寫著感覺那樣沒有什么意義, 不知道各位有什么見解.
泛型類和接口:
寫泛型類的時候只需要在類名后面加上泛型即可, 就像這樣
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class genericclass<t> { public t get(t t){ return t; } public void scr(t t){ system.out.println(t); } public void show(){ genericclass<integer> gc = new genericclass<integer>(); // genericclass<t> gc1 = new genericclass<t>(); gc.get( 3 ); gc.scr( 5 ); //下面2個會報錯 // gc1.get(3); // gc1.scr(5); } } |
從上面的例子中可以看到, 參數化類型是在創建對象的時候具體化的, 那么除此之外, 還可以再什么時候具體化參數化類型呢?
如果是在繼承或實現中, 可以在子類或實現類中確定具體類型
使用java泛型設計通用方法
泛型是java se 1.5的新特性, 泛型的本質是參數化類型, 也就是說所操作的數據類型被指定為一個參數. 因此我們可以利用泛型和反射來設計一些通用方法. 現在有2張表, 一張user表和一張student表.
user:
student:
如果要根據id查詢數據, 你會怎么做呢?寫2個方法分別查詢user和student?其實這時候我們就可以使用泛型和反射寫一個通用的方法.
user的實體類:
1
2
3
4
5
|
private integer id; private string username; private string password; private string hobby; //getxxx方法和setxxx方法 |
student實體類:
1
2
3
4
|
private integer id; private string name; private integer age; //getxxx方法和setxxx方法 |
basedao接口:
1
2
3
4
|
public interface basedao<t> { //根據id查詢的方法 t findbyid(integer id); } |
basedaoimpl類, 實現了basedao接口, 通用方法就在這里面完成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
//設置為抽象的, 不讓他實例化, 讓其子類實例化就行了 //通過泛型設計通用方法的關鍵就是利用反射拿到泛型的具體類型 public abstract class basedaoimpl<t> implements basedao<t> { private string tablename; //表名 private class <t> actualtype; //真實類型 /** * findbyid(integer id)這個方法被子類繼承了, 假設我們在userdaoimpl中操作, 此時參數化類型t為user * 下面的講解都假設是在userdaoimpl中進行的 */ //把公共部分可以放到構造方法中 @suppresswarnings ( "unchecked" ) public basedaoimpl() { //返回類型是type 是 java 編程語言中所有類型的公共高級接口. 它們包括原始類型、參數化類型、數組類型、類型變量和基本類型. //type的已知子接口: parameterizedtype 表示參數化類型, 如 collection<string>. //getclass()得到userdaoimpl的class, 得到class一般有3中方式: getclass(), 類名.class, class.forname() type type = getclass().getgenericsuperclass(); //獲取userdaoimpl<user>的參數化類型的父類basedaoimpl<user> //由于我們得到的是一個參數化類型, 所以轉成parameterizedtype, 因為需要使用里面的方法 parameterizedtype pt = (parameterizedtype) type; //強轉 type[] actualtypearr = pt.getactualtypearguments(); //獲取真實參數類型數組[user.class], 可以調試看到這里的值 actualtype = ( class <t>) actualtypearr[ 0 ]; //數組只有一個元素 tablename = actualtype.getsimplename(); } @override public t findbyid(integer id) { string sql = "select * from " + tablename + " where id = ?" ; try { return qrutils.getqueryrunner().query(sql, new beanhandler<t>(actualtype), id); } catch (sqlexception e) { e.printstacktrace(); } return null ; } } |
連接數據庫操作是用的c3p0和dbutils, 有關這方面的內容可以參看我以前的隨筆.
userdao接口, 繼承basedao接口:
1
2
|
public interface userdao<t> extends basedao<t> { } |
userdaoimpl類, 實現userdao接口, 繼承basedaoimpl類, 可以看到里面什么方法也沒有:
1
2
|
public class userdaoimpl extends basedaoimpl<user> implements userdao<user> { } |
studentdao接口, 繼承basedao接口:
1
2
|
public interface studentdao<t> extends basedao<t> { } |
studentdaoimpl類, 實現studentdao接口, 繼承basedaoimpl類, 也可以看到里面什么方法也沒有:
1
2
|
public class studentdaoimpl extends basedaoimpl<student> implements studentdao<student> { } |
從以上dao可以看到, 我是在繼承basedaoimpl類時把泛型具體化了.
測試:
1
2
3
4
5
6
7
8
9
|
@test public void testgeneric() throws exception { userdao<user> userdao = new userdaoimpl(); system.out.println(userdao.findbyid( 1 )); system.out.println( "-------------------" ); studentdao<student> studentdao = new studentdaoimpl(); system.out.println(studentdao.findbyid( 1 )); } |
看一下測試的結果: 同一個方法把2張表中的數據都查出來了