一:泛型出現(xiàn)的背景在java代碼里,,你會(huì)經(jīng)常發(fā)現(xiàn)類似下邊的代碼: public class Test { public static void main(String[] args) { List list = new ArrayList(); list.add("hah"); //list.add(new Test()); // list.add(1); for (Object object : list) { String s1 = (String)object; //.....如果是你你該如何拿出list的值,如果list中放著上邊的不同類型的東西,。無(wú)解 } } } 編碼的時(shí)候,,不加泛型是可以的,但是 你從容器中拿出來(lái)的時(shí)候必須強(qiáng)制類型轉(zhuǎn)換,,第一是多敲很多代碼,,第二極容易發(fā)生類型轉(zhuǎn)換錯(cuò)誤,這個(gè)運(yùn)行時(shí)異常 比如你把上邊 注釋的代碼放開(kāi),,程序在獲取容器的地方就會(huì)報(bào)運(yùn)行時(shí)異常 ClassCasrException Java語(yǔ)言的設(shè)計(jì)者引入了泛型,,暫時(shí)先不追究它內(nèi)在是怎么實(shí)現(xiàn)的。只需要知道,,如果我們像下邊這么寫,,我們就不需要強(qiáng)制類型轉(zhuǎn)換。我們也不需要擔(dān)心運(yùn)行是異常了,。 List<String> newList = new ArrayList<String>(); newList.add("hhe"); newList.add("123"); String s1 = newList.get(0);//不需要強(qiáng)制類型轉(zhuǎn)換,,因?yàn)槲壹恿朔盒停揖驼J(rèn)為它里邊一定都是String 二: 泛型的語(yǔ)法使用1:使用具體的泛型類型: 尖括號(hào)內(nèi)帶有具體的類型,??梢韵薅ㄟ@個(gè)Map的key和value只能是字符串Map<String, String> map = new HashMap<String, String>(); map.put("key","value"); String value = map.get("key") 從面向?qū)ο蟮慕嵌瓤矗褂脤?duì)象的時(shí)候,,泛型內(nèi)傳入的具體的類型,。聲明的時(shí)候采用尖括號(hào)內(nèi)加占位符的形式,比如這是HashMap的源碼 public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable{ ... public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAllForCreate(m); } ... public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } } 2:方法聲明的時(shí)候 : public <T> T getValue(){...}在上邊的代碼中,,我們可以看到在類上如何定義泛型,也看到了類上定義的占位符在類的普通方法上可以直接使用,。但是如果想在靜態(tài)方法上定義泛型,,這需要單獨(dú)的處理 。下面我們單獨(dú)對(duì)方法上如何定義 和使用泛型進(jìn)行介紹(注意:方法上是否定義泛型和類上是否定義沒(méi)有必然的聯(lián)系) 比如Web項(xiàng)目中,泛型是修飾類型的,,在方法上,,一般就是返回值和參數(shù)列表
public class BaseServiceImpl implements BaseService { protected <T> List<T> calcPage(String hql, PageContext pageContext, Object... params) { int total = getDataTotalNum(hql, params); pageContext.setTotal(total); List<T> list = (List<T>) getPageDataByHQL(hql, pageContext.getRows(), pageContext.getPage(), pageContext.getTotal(), params); return list; } @Override @Sync public void deleteBatchVO(final List<?> dataList) throws ServiceException { baseDAO.deleteBatchVO(dataList); } @Override public List<?> getPageDataByHQL(final String hql, final Map<String, Object> filter) throws ServiceException { return baseDAO.getPageDataByHQL(hql, filter); } } 簡(jiǎn)單的例子: public <T> T TestG(T t){ return t; } 方法定義的時(shí)候,,泛型是這樣設(shè)計(jì),在使用的時(shí)候,,代碼如下: List<TaclUserinfo> list = calcPage(hqluser1.toString(), pageContext, taclRole.getRoleid(), taclRole.getTenantId());//返回值類型 是<T>List<T>的,,j接收的時(shí)候,我直接用List<具體類> 3 :類或者接口使用泛型 interface Collection<V> {..}上邊的HashMap代碼中,,也看到了在類上使用泛型的具體例子,。在真正的項(xiàng)目上,一些基礎(chǔ)的公共類經(jīng)常定義泛型,,如下: 1 public interface GenericDao<T, ID extends Serializable> { 2 3 public abstract void saveOrUpdate(T t) throws DataAccessException; 4 5 public abstract T get(ID id) throws DataAccessException; 6 7 public abstract List<T> query(String queryString) throws DataAccessException; 8 9 public abstract Serializable save(T t) throws DataAccessException;10 11 public abstract void saveOrUpdateAll(Collection<T> entities) throws DataAccessException;12 13 public abstract List<T> loadAll() throws DataAccessException;14 15 public abstract void merge(T t) throws DataAccessException;16 17 } 接口的實(shí)現(xiàn)類: 傳入?yún)?shù)為T,,實(shí)現(xiàn)類中也可以繼續(xù)用T,返回為T也可以用T;實(shí)現(xiàn)的時(shí)候 可以用 extends限定泛型的邊界。 public abstract class GenericDaoImpl<T extends BaseEntity, ID extends Serializable> extends HibernateDaoSupport implements GenericDao<T, ID> { public void merge(T t) throws DataAccessException { TenantInterceptor.setTenantInfoToEntity(t); getHibernateTemplate().merge(t); } public T get(ID id) throws DataAccessException { // getHibernateTemplate().setCacheQueries(true); T load = (T) getHibernateTemplate().get(getEntityClass(), id); return load; } } 具體使用的時(shí)候: public class UserDao extends GenericDaoImpl<User, Serializable> { ...//比如get() merge()這些方法不需要在單獨(dú)編寫,,直接調(diào)用} 4: 聲明帶邊界的泛型 class userDao<T extends BaseEntity>Java中泛型在運(yùn)行期是不可見(jiàn)的,,會(huì)被擦除為它的上級(jí)類型。如果你是無(wú)界的泛型參數(shù)類型,,就會(huì)被替換為Object. public class RedColored<T extends Color> { public T t; public void color(){ t.getColor();//T的邊界是Color,所以可以調(diào)用getColor(),,否則會(huì)編譯報(bào)錯(cuò) } }abstract class Color{ abstract void getColor(); } 類似這樣的定義形式:GenericDaoImpl<T extends BaseEntity, ID extends Serializable> ,java重載了extends,標(biāo)注T的邊界就是BaseEntity,。如果需求是繼承基類,,那么邊界定義在子類上 類似 class Colored2<T extends Color> extends RedColor<T> 5:用于通配符 <?>參考于( Java 通配符解惑 )泛型類型的子類型的不相關(guān)性。比如 現(xiàn)在List<Cat>并不是List<Anilmal>是兩種不同的類型;且無(wú)繼承關(guān)系 ,。那么,,我們像想要傳入的參數(shù)既可能是List<Cat> 也有可能是List<Annimal> public class AnimalTrainer { public void act(List<? extends Animal> list) {//備注:如果用 List<Animal> 作為形參列表,是無(wú)法傳入List<Cat>for (Animal animal : list) { animal.eat(); } } } act(List<,? extends Animal> list),當(dāng)中“,?”就是通配符,而“,? extends Animal”則表示通配符“,?”的上界為Animal,,換句話說(shuō)就是,“,? extends Animal”可以代表Animal或其子類,,可代表不了Animal的父類(如Object),因?yàn)橥ㄅ浞纳辖缡茿nimal,。 所以,,泛型內(nèi)是不存在父子關(guān)系,但是利用通配符可以產(chǎn)生類似的效果:假設(shè)給定的泛型類型為G,,(如List<E>中的List),兩個(gè)具體的泛型參數(shù)X,、Y,當(dāng)中Y是X的子類(如上的Animal和Cat))
三: 泛型可以用到那些地方泛型可以用到容器,方法,,接口,,內(nèi)部類,抽象類 四: Java中泛型獨(dú)特之處泛型是Java1.5之后才引入的,,為了兼容,。Java采用了C++完全不同的實(shí)現(xiàn)思想。Java中的泛型更多的看起來(lái)像是編譯期用的,,比如我定義一個(gè)使用泛型的demo 我在查看它的class文件時(shí),,發(fā)現(xiàn)class文件并沒(méi)有任何泛型信息。 Java會(huì)在編輯期把泛型擦除掉在JAVA的虛擬機(jī)中并不存在泛型,,泛型只是為了完善java體系,,增加程序員編程的便捷性以及安全性而創(chuàng)建的一種機(jī)制,在JAVA虛擬機(jī)中對(duì)應(yīng)泛型的都是確定的類型,,在編寫泛型代碼后,,java虛擬中會(huì)把這些泛型參數(shù)類型都擦除,用相應(yīng)的確定類型來(lái)代替,,代替的這一動(dòng)作叫做類型擦除,,而用于替代的類型稱為原始類型,在類型擦除過(guò)程中,,一般使用第一個(gè)限定的類型來(lái)替換,,若無(wú)限定,則使用Object. 擦除的原理以及邊界關(guān)鍵在于從泛型類型中清除類型參數(shù)的相關(guān)信息,,并且再必要的時(shí)候添加類型檢查和類型轉(zhuǎn)換的方法,。 可以參考Java泛型-類型擦除。 運(yùn)行期編譯期會(huì)去掉泛型信息,,轉(zhuǎn)換為左邊界,,在調(diào)用的地方添加類型轉(zhuǎn)換,。 泛型擦除肯可能導(dǎo)致的問(wèn)題用泛型不可以區(qū)分方法簽名public void test(List<String> ls){ System.out.println("Sting"); } public void test(List<Integer> li){ System.out.println("Integer"); }//這回報(bào)錯(cuò),,編譯期無(wú)法區(qū)分這兩個(gè)方法 泛型類的靜態(tài)變量是共享 public class StaticTest{ public static void main(String[] args){ GT<Integer> gti = new GT<Integer>(); gti.var=1; GT<String> gts = new GT<String>(); gts.var=2; System.out.println(gti.var); } }class GT<T>{ public static int var=0; public void nothing(T x){} } 五: 泛型中特殊使用java中的泛型不只是上述說(shuō)的內(nèi)容,,還有一些特殊的地方,如果這些地方也用泛型該怎么設(shè)計(jì),。比如說(shuō)“動(dòng)態(tài)類型”,,“潛在類型”,“異?!?/p> 程序如果運(yùn)行時(shí)需要類型信息就在調(diào)用的地方傳入類型信息 異常中使用泛型不能拋出也不能捕獲泛型類的對(duì)象,。事實(shí)上,泛型類擴(kuò)展Throwable都不合法,,因?yàn)榉盒托畔?huì)被擦除,,相當(dāng)于catch兩個(gè)相同的異常,是不可以的 數(shù)組與泛型不能聲明參數(shù)化類型的數(shù)組,, 數(shù)組可以記住自己的元素類型,,不能建立一個(gè)泛型數(shù)組。(當(dāng)然 你如果用反射還是可以創(chuàng)建的,,用Array.newInstance,。這里說(shuō)不能建是不能用普通方法) 泛型的一些其他細(xì)節(jié):1.基本類型無(wú)法作為類型參數(shù)即ArrayList<int>這樣的代碼是不允許的,如果為我們想要使用必須使用基本類型對(duì)應(yīng)的包裝器類型ArrayList<Integer> 2.在泛型代碼內(nèi)部,,無(wú)法獲得任何有關(guān)泛型參數(shù)類型的信息換句話說(shuō),,如果傳入的類型參數(shù)為T,即你在泛型代碼內(nèi)部你不知道T有什么方法,,屬性,,關(guān)于T的一切信息都丟失了(類型信息,博文后續(xù)),。 3.注,,在能夠使用泛型方法的時(shí)候,盡量避免使整個(gè)類泛化,。 |
|
來(lái)自: 流曲頻陽(yáng) > 《JAVA學(xué)習(xí)》