Java泛型总结--转
原文地址:https://my.oschina.net/polly/blog/877647
什么是泛型
泛型是jdk5引入的類型機(jī)制,就是將類型參數(shù)化,它是早在1999年就制定的jsr14的實(shí)現(xiàn)。
泛型機(jī)制將類型轉(zhuǎn)換時(shí)的類型檢查從運(yùn)行時(shí)提前到了編譯時(shí),使用泛型編寫的代碼比雜亂的使用object并在需要時(shí)再?gòu)?qiáng)制類型轉(zhuǎn)換的機(jī)制具有更好的可讀性和安全性。
泛型程序設(shè)計(jì)意味著程序可以被不同類型的對(duì)象重用,類似c++的模版。
泛型對(duì)于集合類尤其有用,如ArrayList。這里可能有疑問(wèn),既然泛型為了適應(yīng)不同的對(duì)象,ArrayList本來(lái)就可以操作不同類型的對(duì)象呀?那是因?yàn)闆](méi)有泛型之前采用繼承機(jī)制實(shí)現(xiàn)的,實(shí)際上它只維護(hù)了一個(gè)Object對(duì)象的數(shù)組。結(jié)果就是對(duì)List來(lái)說(shuō)它只操作了一類對(duì)象Object,而在用戶看來(lái)卻可以保存不同的對(duì)象。
泛型提供了更好的解決辦法——類型參數(shù),如:
List<String> list = new ArrayList<String>();這樣解決了幾個(gè)問(wèn)題:
1 可讀性,從字面上就可以判斷集合中的內(nèi)容類型; 2 類型檢查,避免插入非法類型。 3 獲取數(shù)據(jù)時(shí)不在需要強(qiáng)制類型轉(zhuǎn)換。泛型類
public class Pair<T>{ private T field1; }其中?<T>?是類型參數(shù)定義。
使用時(shí):Pair<String> p = new Pair<String>();
此時(shí)類內(nèi)部的field1就是字符串類型了。
如果引用多個(gè)類型,可以使用逗號(hào)分隔:<S, D>
類型參數(shù)名可以使用任意字符串,建議使用有代表意義的單個(gè)字符,以便于和普通類型名區(qū)分,如:T代表type,有原數(shù)據(jù)和目的數(shù)據(jù)就用S,D,子元素類型用E等。當(dāng)然,你也可以定義為XYZ,甚至xyZ。
泛型方法
泛型方法定義如下:
public static <T> T marshalle(T arg){}與泛型類一樣,<T>?是類型參數(shù)定義。如:
public class GenericMethod { public static <T> T getMiddle(T... a){ return a[a.length/2]; } }嚴(yán)格的調(diào)用方式:
String o=GenericMethod.<String>getMiddle("213","result","12");一般情況下調(diào)用時(shí)可以省略,看起來(lái)就像定義String類型參數(shù)的方法:GenericMethod.getMiddle(String,String,String),這是因?yàn)閖dk會(huì)根據(jù)參數(shù)類型進(jìn)行推斷。看一下下面的例子:
Object o=GenericMethod.getMiddle("213",0,"12"); System.out.println(o.getClass()); System.out.println(o);輸出結(jié)果為:
class java.lang.Integer 0這是因?yàn)閖dk推斷三個(gè)參數(shù)的共同父類,匹配為Object,那么相當(dāng)于:
Object o=GenericMethod.<Object>getMiddle("213",0,"12");習(xí)慣了類型參數(shù)放在類的后面,如ArrayList<String>,泛型方法為什么不放在后面?看一個(gè)例子:
public static <T,S> T f(T t){return t;} public static class a{} public static class b{} //盡量惡心一點(diǎn)因此,為了避免歧義,jdk采用類型限定符前置。
泛型方法與泛型類的方法
如果泛型方法定義在泛型類中,而且類型參數(shù)一樣:
public class GenericMethod<T> { public <T> void sayHi(T t){ System.out.println("Hi "+t); } }是不是說(shuō),定義GenericMethod時(shí)傳了 Integer 類型,sayHi()也就自動(dòng)變成 Integer 了呢?No。
String i="abc"; new GenericMethod<Integer>().<String>sayHi(i);該代碼運(yùn)行一點(diǎn)問(wèn)題都沒(méi)有。原因就在于泛型方法中的<T>,如果去掉它,就有問(wèn)題了。
The method sayHi(Integer) in the type GenericMethod<Integer> is not applicable for the arguments (String)小結(jié):
泛型方法有自己的類型參數(shù),泛型類的成員方法使用的是當(dāng)前類的類型參數(shù)。
方法中有<T>?是泛型方法;沒(méi)有的,稱為泛型類中的成員方法。
類型參數(shù)的限定
如果限制只有特定某些類可以傳入T參數(shù),那么可以對(duì)T進(jìn)行限定,如:只有實(shí)現(xiàn)了特定接口的類:<T extends Comparable>,表示的是Comparable及其子類型。
為什么是extends不是?implements,或者其他限定符?
嚴(yán)格來(lái)講,該表達(dá)式意味著:`T subtypeOf Comparable`,jdk不希望再引入一個(gè)新的關(guān)鍵詞;其次,T既可以是類對(duì)象也可以是接口,如果是類對(duì)象應(yīng)該是`implements`,而如果是接口,則應(yīng)該是`extends`;從子類型上來(lái)講,extends更接近要表達(dá)的意思。好吧,這是一個(gè)約定。限定符可以指定多個(gè)類型參數(shù),分隔符是?&,不是逗號(hào),因?yàn)樵陬愋蛥?shù)定義中,逗號(hào)已經(jīng)作為多個(gè)類型參數(shù)的分隔符了,如:<T,S extends Comparable & Serializable>。
泛型限定的優(yōu)點(diǎn):
限制某些類型的子類型可以傳入,在一定程度上保證類型安全;
可以使用限定類型的方法。如:
public class Parent<T>{ private T name; public T getName() { return name; } public void setName(T name) { //這里只能使用name自object繼承的方法 this.name = name; } }加上限定符,就可以訪問(wèn)限定類型的方法了,類型更明確。
public class Parent<T extends List<T>>{ private T name; public T getName() { return name; } public void setName(T name) { //這里可以訪問(wèn)List的方法,如name.size() this.name = name; } }注:
我們知道final類不可繼承,在繼承機(jī)制上class SomeString extends String是錯(cuò)誤的,但泛型限定符使用時(shí)是可以的:<T extends String>,只是會(huì)給一個(gè)警告。
后面的通配符限定有一個(gè)super關(guān)鍵字,這里沒(méi)有。
泛型擦除
泛型只在編譯階段有效,編譯后類型被擦除了,也就是說(shuō)jvm中沒(méi)有泛型對(duì)象,只有普通對(duì)象。所以完全可以把代碼編譯為jdk1.0可以運(yùn)行的字節(jié)碼。
擦除的方式
定義部分,即尖括號(hào)中間的部分直接擦除。
public class GenericClass<T extends Comparable>{}擦除后:
public class GenericClass{}引用部分如:
public T field1;其中的T被替換成對(duì)應(yīng)的限定類型,擦除后:
public Comparable field1;如果沒(méi)有限定類型:
public class GenericClass<T>{ public T field1; }那么的替換為object,即:
public class GenericClass{ public Object field1; }有多個(gè)限定符的,替換為第一個(gè)限定類型名。如果引用了第二個(gè)限定符的類對(duì)象,編譯器會(huì)在必要的時(shí)候進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
public class GenericClass<T extends Comparable & Serializable>{ public T field1; }類擦除后變?yōu)?#xff1a;
public class GenericClass{ public Comparable field1; }而表達(dá)式返回值返回時(shí),泛型的編譯器自動(dòng)插入強(qiáng)制類型轉(zhuǎn)換。
泛型擦除的殘留
反編譯GenericClass:
Compiled from "GenericClass.java" public class com.pollyduan.generic.GenericClass<T> { public T field1; public com.pollyduan.generic.GenericClass(); }好像前面說(shuō)的不對(duì)啊,這還是T啊,沒(méi)有擦除呀?
這就是擦除的殘留。反匯編:
{ public T field1; descriptor: Ljava/lang/Object; flags: ACC_PUBLIC Signature: #8 // TT; public com.pollyduan.generic.GenericClass(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #12 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 2: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/pollyduan/generic/GenericClass; LocalVariableTypeTable: Start Length Slot Name Signature 0 5 0 this Lcom/pollyduan/generic/GenericClass<TT;>; } SourceFile: "GenericClass.java" Signature: #22 // <T:Ljava/lang/Object;>Ljava/lang/Object;其中:
descriptor:對(duì)方法參數(shù)和返回值進(jìn)行描述; signature:泛型類中獨(dú)有的標(biāo)記,普通類中沒(méi)有,JDK5才加入,標(biāo)記了定義時(shí)的成員簽名,包括定義時(shí)的泛型參數(shù)列表,參數(shù)類型,返回值等;
可以看到public T field1;是簽名,還保留了定義的格式;其對(duì)應(yīng)的參數(shù)類型是Ljava/lang/Object;。
最后一行是類的簽名,可以看到T后面有跟了擦除后的參數(shù)類型:<T:Ljava/lang/Object;>。
這樣的機(jī)制,對(duì)于分析字節(jié)碼是有意義的。
泛型的約束和限制
不能使用8個(gè)基本類型實(shí)例化類型參數(shù)
原因在于類型擦除,Object不能存儲(chǔ)基本類型:
byte,char,short,int,long,float,double,boolean
從包裝類角度來(lái)看,或者說(shuō)三個(gè): Number(byte,short,int,long,float,double),char,boolean
類型檢查不可使用泛型
if(aaa instanceof Pair<String>){}//error Pair<String> p = (Pair<String>) a;//warn Pair<String> p; Pair<Integer> i; i.getClass()==p.getClass();//true不能創(chuàng)建泛型對(duì)象數(shù)組
GenericMethod<User>[] o=null;//ok o=new GenericMethod<User>[10];//error可以定義泛型類對(duì)象的數(shù)組變量,不能創(chuàng)建及初始化。
注,可以創(chuàng)建通配類型數(shù)組,然后進(jìn)行強(qiáng)制類型轉(zhuǎn)換。不過(guò)這是類型不安全的。
o=(GenericMethod<User>[]) new GenericMethod不可以創(chuàng)建的原因是:因?yàn)轭愋筒脸脑驘o(wú)法在為元素賦值時(shí)類型檢查,因此jdk強(qiáng)制不允許。
有一個(gè)特例是方法的可變參數(shù),雖然本質(zhì)上是數(shù)組,卻可以使用泛型。
安全的方法是使用List。
Varargs警告
java不支持泛型類型的對(duì)象數(shù)組,可變參數(shù)是可以的。它也正是利用了強(qiáng)制類型轉(zhuǎn)換,因此同樣是類型不安全的。所以這種代碼編譯器會(huì)給一個(gè)警告。
public static <T> T getMiddle(T... a){ return a[a.length/2]; }去除警告有兩種途徑:一種是在定義可變參數(shù)方法上(本例中的getMiddle())加上@SafeVarargs注解,另一種是在調(diào)用該方法時(shí)添加@SuppressWarnings("unchecked")注解。
不能實(shí)例化泛型對(duì)象
T t= new T();//error T.class.newInstance();//error T.class;//error解決辦法是傳入Class<T> t參數(shù),調(diào)用t.newInstance()。
public void sayHi(Class<T> c){ T t=null; try { t=c.newInstance(); } catch (Exception e) { e.printStackTrace(); } System.out.println("Hi "+t); }不能在泛型類的靜態(tài)域中使用泛型類型
public class Singleton<T>{ private static T singleton; //error public static T getInstance(){} //error public static void print(T t){} //error }但是,靜態(tài)的泛型方法可以使用泛型類型:
public static <T> T getInstance(){return null;} //ok public static <T> void print(T t){} //ok這個(gè)原因很多資料中都沒(méi)說(shuō)的太明白,說(shuō)一下個(gè)人理解,僅供參考:
1. 泛型類中,<T>稱為類型變量,實(shí)際上就相當(dāng)于在類中隱形的定義了一個(gè)不可見(jiàn)的成員變量:`private T t;`,這是對(duì)象級(jí)別的,對(duì)于泛型類型變量來(lái)說(shuō)是在對(duì)象初始化時(shí)才知道其具體類型的。而在靜態(tài)域中,不需要對(duì)象初始化就可以調(diào)用,這是矛盾的。 2. 靜態(tài)的泛型方法,是在方法層面定義的,就是說(shuō)在調(diào)用方法時(shí),T所指的具體類型已經(jīng)明確了。不能捕獲泛型類型的對(duì)象
Throwable類不可以被繼承,自然也不可能被catch。
public class GenericThrowable<T> extends Throwable{ //The generic class GenericThrowable<T> may not subclass java.lang.Throwable }但由于Throwable可以用在泛型類型參數(shù)中,因此可以變相的捕獲泛型的Throwable對(duì)象。
這個(gè)能干什么?
FileReader實(shí)例化可能拋出已檢查異常,jdk中要求必須捕獲或者拋出已檢查異常。這種模式把它給隱藏了。也就是說(shuō)可以消除已檢查異常,有點(diǎn)不地道,顛覆了java異常處理的認(rèn)知,后果不可預(yù)料,慎用。
擦除的沖突
重載與重寫
定義一個(gè)普通的父類:
package com.pollyduan.generic;public class Parent{ public void setName(Object name) { System.out.println("Parent:" + name); } }那么繼承一個(gè)子類,Son.java
package com.pollyduan.generic;public class Son extends Parent { public void setName(String name) { System.out.println("son:" + name); } public static void main(String[] args) { Son son=new Son(); son.setName("abc"); son.setName(new Object()); } }Son類重載了一個(gè)setName(String)方法,這沒(méi)問(wèn)題。輸出:
son:abc Parent:java.lang.Object@6d06d69cParent修改泛型類:
package com.pollyduan.generic;public class Parent<T>{ public void setName(T name) { System.out.println("Parent:" + name); } }從擦除的機(jī)制得知,擦除后的class文件為:
package com.pollyduan.generic;public class Parent{ public void setName(Object name) { System.out.println("Parent:" + name); } }這和最初的非泛型類是一樣的,那么Son類修改為:
package com.pollyduan.generic;public class Son extends Parent<String> { public void setName(String name) { System.out.println("son:" + name); } public static void main(String[] args) { Son son=new Son(); son.setName("abc"); son.setName(new Object());//The method setName(String) in the type Son is not applicable for the arguments (Object) } }發(fā)現(xiàn)重載無(wú)效了。這是泛型擦除造成的,無(wú)論是否在setName(String)是否標(biāo)注為@Override都將是重寫,都不是重載。而且,即便你不寫setName(String)方法,編譯器已經(jīng)默認(rèn)重寫了這個(gè)方法。
換一個(gè)角度來(lái)考慮,定義Son時(shí),Parent已經(jīng)明確了類型參數(shù)為String,那么再寫setName(Stirng)是重寫,也是合理的。
package com.pollyduan.generic;public class Son extends Parent<String> { public static void main(String[] args) { Son son=new Son(); son.setName("abc");//ok } }反編譯會(huì)發(fā)現(xiàn),編譯器在內(nèi)部編譯了兩個(gè)方法:
public void setName(java.lang.String); public void setName(java.lang.Object);setName(java.lang.Object)?雖然是public但編碼時(shí)會(huì)發(fā)現(xiàn)不可見(jiàn),它稱為"橋方法",它會(huì)重寫父類的方法。
Son son=new Son(); Parent p=son; p.setName(new Object());強(qiáng)行調(diào)用會(huì)轉(zhuǎn)換異常,也就證明了它實(shí)際上調(diào)用的是son的setName(String)。
我非要重載怎么辦?只能曲線救國(guó),改個(gè)名字吧。
public void setName2(String name) { System.out.println("son:" + name); }繼承泛型的參數(shù)化
一個(gè)泛型類的類型參數(shù)不同,稱之為泛型的不同參數(shù)化。
泛型有一個(gè)原則:一個(gè)類或類型變量不可成為兩個(gè)不同參數(shù)化的接口類型的子類型。如:
package com.pollyduan.generic;import java.util.Comparator;public class Parent implements Comparator{這樣是沒(méi)有問(wèn)題的。如果增加了泛型參數(shù)化:
package com.pollyduan.generic;import java.util.Comparator;public class Parent implements Comparator<Parent>{原因是Son實(shí)現(xiàn)了兩次Comparator<T>,擦除后均為Comparator<Object>,造成了沖突。
通配符類型
通配符是在泛型類使用時(shí)的一種機(jī)制,不能用在泛型定義時(shí)的泛型表達(dá)式中(這是泛型類型參數(shù)限定符)。
子類型通配符
如果P是S的超類,那么?Pair<S>就是Pair<? extends P>的子類型,通配符就是為了解決這個(gè)問(wèn)題的。
這稱為子類型限定通配符,又稱上邊界通配符(upper bound wildcard Generics),代表繼承它的所有子類型,通配符匹配的類型不允許作為參數(shù)傳入,只能作為返回值。
public static void test1() { Parent<Integer> bean1 = new Parent<Integer>(); bean1.setName(123); ParentgetName()的合理性:
無(wú)論bean2指向的是任何類型的對(duì)象,只要是Number的子類型,都可以用Number類型變量接收。為什么setName(str)會(huì)拋出異常呢?
1.超類型通配符
與之對(duì)應(yīng)的是超類型 Pair<? super P>,又稱下邊界通配符(lower bound wildcard Generics),通配符匹配的類型可以為方法提供參數(shù),不能得到返回值。
public static void test2() { public static void test2() { Parent<Number> bean1 = new Parent<Number>(); bean1.setName(123); Parent<? super Integer> bean2 = bean1; Integer i = 100; bean2.setName(i); Integer s = bean2.getName();// 編譯錯(cuò)誤 Object o = bean2.getName();// ok System.out.println(o); } }setName的可行性:
1. 無(wú)論bean2指向Parent<Number>,Parent<Integer>還是Parent<Object>都是允許的; 2. 都可以傳入Integer或Integer的子類型。getName為毛報(bào)錯(cuò)?
1. 由于限定類型的超類可能有很多,getName返回類型不可預(yù)知,如Integer 或其父類型Number/OtherParentClass...都無(wú)法保證類型檢查的安全。2. 但是由于Java的所有對(duì)象的頂級(jí)祖先類都是Object,因此可以用Object獲取getName返回值。無(wú)限定通配符
Pair<?>?就是?Pair<? extends Object>
因此,無(wú)限定通配符可以作為返回值,不可做入?yún)ⅰ?/p>
返回值只能保存在Object中。
P<?>?和P
Pair可以調(diào)用setter方法,這是它和Pair<?>最重要的區(qū)別。
P<?>?不等于?P<Object>
P<Object>是P<?>的子類。
類型通配符小結(jié)
1. 限定通配符總是包括自己; 2. 子類型通配符:set方法受限,只可讀,不可寫; 3. 超類型通配符:get方法受限,不可讀(Object除外),只可寫; 4. 無(wú)限定通配符,只可讀不可寫; 5. 如果你既想存,又想取,那就別用通配符; 6. 不可同時(shí)聲明子類型和超類型限定符,及extends和super只能出現(xiàn)一個(gè)。通配符的受限只針對(duì)setter(T)和T getter(),如果定義了一個(gè)setter(Integer)這種具體類型參數(shù)的方法,無(wú)限制。
通配符捕獲
通配符限定類中可以使用T,編譯器適配類型。
有一個(gè)鍵值對(duì)的泛型類:
使用通配類型創(chuàng)建一個(gè)swap方法交換key-value,交換時(shí)需要先使用一個(gè)臨時(shí)變量保存一個(gè)字段:
public static void swap(Pair<?> p){ // ? k=p.getKey();//error,?不可作為具體類型限定符 Object k=p.getKey();//好吧,換成object,ok p.setKey(p.getValue());//but,通配符類型不可做入?yún)?p.setValue(k); }這里有一個(gè)辦法解決它,再封裝一個(gè)swapHelper():
private static <T> void swapHelper(Pair<T> p){ T k=p.getKey(); p.setKey(p.getValue()); p.setValue(k); } public static void swap(Pair<?> p){ swapHelper(p); }這種方式,稱為:通配符捕獲,用一個(gè)Pair<T>?來(lái)捕獲?Pair<?>中的類型。
注:
當(dāng)然,你完全可以直接使用swapHelper,這里只是為了說(shuō)明這樣一種捕獲機(jī)制。只允許捕獲單個(gè)、確定的類型,如:ArrayList<Pair<?泛型與繼承
繼承的原則
繼承泛型類時(shí),必須對(duì)父類中的類型參數(shù)進(jìn)行初始化。或者說(shuō)父類中的泛型參數(shù)必須在子類中可以確定具體類型。
例如:有一個(gè)泛型類Parent<T>,那么Son類定義時(shí)有兩種方式初始化父類型的類型參數(shù):
1 用具體類型初始化:
public class Son extends Parent<String>{}2 用子類中的泛型類型初始化父類:
public class Son<T> extends Parent<T>{}Pair<P>和Pair<S>
無(wú)論P(yáng)和S有什么繼承關(guān)系,一般Pair<P>和Pair<S>沒(méi)什么關(guān)系。
Pair<Son> s=new Pair<>(); Pair<Parent> p=s;//errorParent<T>和Son<T>
泛型類自身可以繼承其他類或?qū)崿F(xiàn)接口,如 List<T>實(shí)現(xiàn)ArrayList<T>
泛型類可以擴(kuò)展泛型類或接口,如ArrayList<T> 實(shí)現(xiàn)了 List<T>,此時(shí)ArrayList<T>可以轉(zhuǎn)換為L(zhǎng)ist<T>。這是安全的。
Parent<T>和Parent
Parent<T>隨時(shí)都可以轉(zhuǎn)換為原生類型Parent,但需要注意類型檢查的安全性。
package com.pollyduan.generic;import java.io.File;class Parent<T> { private T name; public T getName() { return name; } public void setName(T name) { this.name = name; } public static void main(String[] args) { Parent<String> p1=new Parent<>(); p1.setName("tom"); System.out.println(p1.getName()); Parent p2=p1; p2.setName(new File("1.txt"));//嚴(yán)重error System.out.println(p2.getName()); } }運(yùn)行沒(méi)有異常,注意。
Person<? extends XXX>
嚴(yán)格講通配符限定的泛型對(duì)象不屬于繼承范疇,但使用中有類似繼承的行為。
Son是Parent的子類型,那么Person<? extends Son>就是Person<? extends Parent>?的子類型。
Person<? extends Object>?等同于?Person<?>,那么基于上以規(guī)則可以推斷:Person<? extends Parent>?是?Person<?>?的子類型。
Person<Object>?是?Person<?>?的子類型。
泛型與反射
泛型相關(guān)的反射
有了泛型機(jī)制,jdk的reflect包中增加了幾個(gè)泛型有關(guān)的類:
Class<T>.getGenericSuperclass()獲取泛型超類ParameterizedType類型參數(shù)實(shí)體類實(shí)例
基于泛型的通用JDBC DAO。
User.java
package com.pollyduan.generic;AbstractBaseDaoImpl.java
package com.pollyduan.generic;public abstract class AbstractBaseDaoImpl<T> { public AbstractBaseDaoImpl() { Type t = getClass().getGenericSuperclass(); System.out.println(t); } }UserDaoImpl.java
package com.pollyduan.generic;public class UserDaoImpl extends AbstractBaseDaoImpl<User> { public static void main(String[] args) { UserDaoImpl userDao=new UserDaoImpl(); } }運(yùn)行UserDaoImpl.main(),輸出:
com.pollyduan.generic.AbstractBaseDaoImpl<com.pollyduan.generic.User>可以看到,在抽象類AbstractBaseDaoImpl中可以拿到泛型類的具體類。
從這一機(jī)制,可以通過(guò)AbstractBaseDaoImpl實(shí)現(xiàn)通用的JDBA DAO。
完善AbstractBaseDaoImpl.java
package com.pollyduan.generic;import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; public abstract class AbstractBaseDaoImpl<T, K> { private Class<T> entityClass; private Class<T> primaryKeyClass; public AbstractBaseDaoImpl() { Type t = getClass().getGenericSuperclass(); ParameterizedType pt = (ParameterizedType) t; Type[] typeParameters = pt.getActualTypeArguments(); entityClass = (Class<T>) typeParameters[0]; primaryKeyClass = (Class<T>) typeParameters[1]; } public void save(T t) { StringBuilder sb = new StringBuilder("INSERT INTO "); sb.append(entityClass.getSimpleName()); sb.append("("); Field[] fields = entityClass.getDeclaredFields(); String fieldNames = Arrays.asList(fields).stream().map(x -> x.getName()).collect(Collectors.joining(",")); sb.append(fieldNames); sb.append(") VALUES("); sb.append(fieldNames.replaceAll("[^,]+", "?")); sb.append(")"); System.out.println(sb.toString()); //根據(jù)反射還要遍歷fields處理變量綁定,略。 } public void delete(K k) { StringBuilder sb = new StringBuilder("DELETE FROM "); sb.append(entityClass.getSimpleName()); sb.append(" WHERE ID=?");// 這里默認(rèn)主鍵名為id,應(yīng)該配合注解動(dòng)態(tài)獲取主鍵名 System.out.println(sb.toString()); } public void update(T t) { StringBuilder sb = new StringBuilder("UPDATE "); sb.append(entityClass.getSimpleName()); sb.append(" SET "); Field[] fields = entityClass.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().toLowerCase().equals("id")) { continue; } sb.append(fields[i].getName()); sb.append("=?"); if (i < fields.length - 1) { sb.append(","); } } sb.append(" WHERE ID=?"); System.out.println(sb.toString()); } public T get() throws Exception { T t = null; // 模擬resultset Map<String, Object> rs = new HashMap<>(); t = entityClass.newInstance(); Field[] fields = entityClass.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); field.set(t, rs.get(field.getName())); } return t; } public static void main(String[] args) { UserDaoImpl userDao=new UserDaoImpl(); User user1=new User(); userDao.save(user1); userDao.delete(1); userDao.update(user1); try { User user2=userDao.get(); System.out.println(user2); } catch (Exception e) { e.printStackTrace(); } } }有現(xiàn)成的ORM框架可用,這里就意思意思得了。輸出:
INSERT INTO User(id,name) VALUES(?,?) DELETE FROM User WHERE ID=? UPDATE User SET name=? WHERE ID=? User(id=1, name=Peter)轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/6707861.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Java泛型总结--转的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: spring boot整合mail
- 下一篇: 防雪崩利器:熔断器 Hystrix 的原