Java 泛型在實際開發中的應用

  java泛型是對Java語言的類型系統的一種擴展,泛型的本質就是將所操做的數據類型參數化。下面我會由淺入深地介紹Java的泛型。java

一:泛型出現的背景

在java代碼裏,你會常常發現相似下邊的代碼:linux

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中放着上邊的不一樣類型的東西。無解
        }
    }
}

  編碼的時候,不加泛型是能夠的,可是 你從容器中拿出來的時候必須強制類型轉換,第一是多敲不少代碼,第二極容易發生類型轉換錯誤,這個運行時異常 好比你把上邊程序員

註釋的代碼放開,程序在獲取容器的地方就會報運行時異常 ClassCasrException編程

Java語言的設計者引入了泛型,暫時先不追究它內在是怎麼實現的。只須要知道,若是咱們像下邊這麼寫,咱們就不須要強制類型轉換。咱們也不須要擔憂運行是異常了。數組

List<String> newList = new ArrayList<String>();
newList.add("hhe");
newList.add("123");
String s1 = newList.get(0);//不須要強制類型轉換,由於我加了泛型,我就認爲它裏邊必定都是String

二: 泛型的語法使用

1:使用具體的泛型類型: 尖括號內帶有具體的類型。能夠限定這個Map的key和value只能是字符串

Map<String, String> map = new HashMap<String, String>();
map.put("key","value");
String value = map.get("key")

從面向對象的角度看,使用對象的時候,泛型內傳入的具體的類型。聲明的時候採用尖括號內加佔位符的形式,好比這是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:方法聲明的時候 : public  <T> T getValue(){...}

  在上邊的代碼中,咱們能夠看到在類上如何定義泛型,也看到了類上定義的佔位符在類的普通方法上能夠直接使用。可是若是想在靜態方法上定義泛型,這須要單獨的處理  。下面咱們單獨對方法上如何定義ide

和使用泛型進行介紹(注意:方法上是否認義泛型和類上是否認義沒有必然的聯繫)this

好比Web項目中,泛型是修飾類型的,在方法上,通常就是返回值和參數列表編碼

  •   返回值類型:能夠定義爲List<String>等形式,可是實際開發中,通常都是不知道具體類型,定義形式以下  <T> List<T> test(T t){...} ,前邊的<T>能夠理解爲泛型的聲明,你只有聲明瞭T,你才能夠在              方法中用到T,這一具體的類型, List<T>是具體的返回值類型。
  •   方法傳參: 能夠用佔位符限定的容器 好比 List<T>,或者直接是佔位符 T  
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);
    }
}

簡單的例子:spa

public <T> T TestG(T t){
        return t;
    }

 方法定義的時候,泛型是這樣設計,在使用的時候,代碼以下:

List<TaclUserinfo> list = calcPage(hqluser1.toString(), pageContext,
                taclRole.getRoleid(), taclRole.getTenantId());
//返回值類型 是<T>List<T>的,j接收的時候,我直接用List<具體類>

3 :類或者接口使用泛型  interface Collection<V> {..}

  上邊的HashMap代碼中,也看到了在類上使用泛型的具體例子。在真正的項目上,一些基礎的公共類常常定義泛型,以下:

 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 }    

接口的實現類: 傳入參數爲T,實現類中也能夠繼續用T,返回爲T也能夠用T;實現的時候 能夠用 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;
    }    
}

具體使用的時候:

public class UserDao extends GenericDaoImpl<User, Serializable> {
    ...//好比get() merge()這些方法不須要在單獨編寫,直接調用
}    

4:  聲明帶邊界的泛型   class userDao<T extends BaseEntity>

  Java中泛型在運行期是不可見的,會被擦除爲它的上級類型。若是你是無界的泛型參數類型,就會被替換爲Object. 

public class RedColored<T extends Color> {
    public T t;
    public void color(){
        t.getColor();//T的邊界是Color,因此能夠調用getColor(),不然會編譯報錯
    }
}

abstract class Color{
    abstract void getColor();
}

   相似這樣的定義形式:GenericDaoImpl<T extends BaseEntity, ID extends Serializable> ,java重載了extends,標註T的邊界就是BaseEntity。若是需求是繼承基類,那麼邊界定義在子類上

相似 

class Colored2<T extends Color> extends RedColor<T>

5:用於通配符  <?>

   參考於( Java 通配符解惑  )泛型類型的子類型的不相關性。好比 如今List<Cat>並非List<Anilmal>是兩種不一樣的類型;且無繼承關係 。那麼,咱們像想要傳入的參數既多是List<Cat>

也有多是List<Annimal>

public class AnimalTrainer {
    public void act(List<? extends Animal> list) {
//備註:若是用 List<Animal> 做爲形參列表,是沒法傳入List<Cat>for (Animal animal : list) {
            animal.eat();
        }
    }
}

  act(List<? extends Animal> list),當中「?」就是通配符,而「? extends Animal」則表示通配符「?」的上界爲Animal,換句話說就是,「? extends Animal」能夠表明Animal或其子類,可表明不了Animal的父類(如Object),由於通配符的上界是Animal。

因此,泛型內是不存在父子關係,可是利用通配符能夠產生相似的效果:

假設給定的泛型類型爲G,(如List<E>中的List),兩個具體的泛型參數X、Y,當中Y是X的子類(如上的Animal和Cat))

  • G<? extends Y> 是 G<? extends X>的子類型(如List<? extends Cat> 是 List<? extends Animal>的子類型)。
  • G<X> 是 G<? extends X>的子類型(如List<Animal> 是 List<? extends Animal>的子類型)
  • G<?> 與 G<? extends Object>等同,如List<?> 與List<? extends Objext>等同 

三: 泛型能夠用到那些地方

    泛型能夠用到容器,方法,接口,內部類,抽象類

四: Java中泛型獨特之處

    泛型是Java1.5以後才引入的,爲了兼容。Java採用了C++徹底不一樣的實現思想。Java中的泛型更多的看起來像是編譯期用的,好比我定義一個使用泛型的demo

我在查看它的class文件時,發現class文件並無任何泛型信息。

Java會在編輯期把泛型擦除掉

  在JAVA的虛擬機中並不存在泛型,泛型只是爲了完善java體系,增長程序員編程的便捷性以及安全性而建立的一種機制,在JAVA虛擬機中對應泛型的都是肯定的類型,在編寫泛型代碼後,java虛擬中會把這些泛型參數類型都擦除,用相應的肯定類型來代替,代替的這一動做叫作類型擦除,而用於替代的類型稱爲原始類型,在類型擦除過程當中,通常使用第一個限定的類型來替換,若無限定,則使用Object.

擦除的原理以及邊界

  關鍵在於從泛型類型中清除類型參數的相關信息,而且再必要的時候添加類型檢查類型轉換的方法。

  能夠參考Java泛型-類型擦除。 運行期編譯期會去掉泛型信息,轉換爲左邊界,在調用的地方添加類型轉換。

泛型擦除肯可能致使的問題

用泛型不能夠區分方法簽名

public void test(List<String> ls){
                System.out.println("Sting");
            }
            public void test(List<Integer> li){
                System.out.println("Integer");
            }
//這回報錯,編譯期沒法區分這兩個方法

泛型類的靜態變量是共享

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中的泛型不僅是上述說的內容,還有一些特殊的地方,若是這些地方也用泛型該怎麼設計。好比說「動態類型」,「潛在類型」,「異常」

程序若是運行時須要類型信息

  就在調用的地方傳入類型信息

異常中使用泛型

  不能拋出也不能捕獲泛型類的對象。事實上,泛型類擴展Throwable都不合法,由於泛型信息會被擦除,至關於catch兩個相同的異常,是不能夠的

數組與泛型

  不能聲明參數化類型的數組, 數組能夠記住本身的元素類型,不能創建一個泛型數組。(固然 你若是用反射仍是能夠建立的,用Array.newInstance。這裏說不能建是不能用普通方法)

泛型的一些其餘細節:  

  1.基本類型沒法做爲類型參數即ArrayList<int>這樣的代碼是不容許的,若是爲咱們想要使用必須使用基本類型對應的包裝器類型ArrayList<Integer>

  2.在泛型代碼內部,沒法得到任何有關泛型參數類型的信息換句話說,若是傳入的類型參數爲T,即你在泛型代碼內部你不知道T有什麼方法,屬性,關於T的一切信息都丟失了(類型信息,博文後續)。

  3.注,在可以使用泛型方法的時候,儘可能避免使整個類泛化。

六:簡單歸納

  虛擬機中沒有泛型,只有普通類和普通方法

  全部泛型類的類型參數在編譯時都會被擦除

  建立泛型對象時請指明類型,讓編譯器儘早的作參數檢查

  要忽略編譯器的警告信息,那意味着潛在的ClassCastException等着你。

相關文章
相關標籤/搜索