當前位置:
首頁 > 知識 > Java 泛型在實際開發中的應用

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

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

一:泛型出現的背景

在java代碼里,你會經常發現類似下邊的代碼:

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 newList = new ArrayList;
newList.add("hhe");
newList.add("123");
String s1 = newList.get(0);//不需要強制類型轉換,因為我加了泛型,我就認為它裡邊一定都是String

二: 泛型的語法使用1:使用具體的泛型類型: 尖括弧內帶有具體的類型。可以限定這個Map的key和value只能是字元串

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

從面向對象的角度看,使用對象的時候,泛型內傳入的具體的類型。聲明的時候採用尖括弧內加佔位符的形式,比如這是HashMap的源碼

public class HashMap
extends AbstractMap
implements Map, Cloneable, Serializable{
...
public HashMap(Map 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 e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
}

2:方法聲明的時候 : public T getValue{...}

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

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

比如Web項目中,泛型是修飾類型的,在方法上,一般就是返回值和參數列表

  • 返回值類型:可以定義為List等形式,但是實際開發中,一般都是不知道具體類型,定義形式如下 List test(T t){...} ,前邊的可以理解為泛型的聲明,你只有聲明了T,你才可以在 方法中用到T,這一具體的類型, List是具體的返回值類型。
  • 方法傳參: 可以用佔位符限定的容器 比如 List,或者直接是佔位符 T

public class BaseServiceImpl implements BaseService {
protected List calcPage(String hql, PageContext pageContext,
Object... params) {
int total = getDataTotalNum(hql, params);
pageContext.setTotal(total);
List list = (List) 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 filter) throws ServiceException {
return baseDAO.getPageDataByHQL(hql, filter);
}
}

簡單的例子:

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

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

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

3 :類或者介面使用泛型 interface Collection {..}

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

1 public interface GenericDao {
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 query(String queryString) throws DataAccessException;
8
9 public abstract Serializable save(T t) throws DataAccessException;
10
11 public abstract void saveOrUpdateAll(Collection entities) throws DataAccessException;
12
13 public abstract List loadAll throws DataAccessException;
14
15 public abstract void merge(T t) throws DataAccessException;
16
17 }

介面的實現類: 傳入參數為T,實現類中也可以繼續用T,返回為T也可以用T;實現的時候 可以用 extends限定泛型的邊界。

public abstract class GenericDaoImpl extends
HibernateDaoSupport implements GenericDao {

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 {
...//比如get merge這些方法不需要在單獨編寫,直接調用
}

4: 聲明帶邊界的泛型 class userDao

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

public class RedColored {
public T t;
public void color{
t.getColor;//T的邊界是Color,所以可以調用getColor,否則會編譯報錯
}
}

abstract class Color{
abstract void getColor;
}

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

類似

class Colored2 extends RedColor

5:用於通配符

參考於( Java 通配符解惑)泛型類型的子類型的不相關性。比如 現在List並不是List兩種不同的類型;且無繼承關係 。那麼,我們像想要傳入的參數既可能是List

也有可能是List

public class AnimalTrainer {
public void act(List list) {
//備註:如果用 List 作為形參列表,是無法傳入Listfor (Animal animal : list) {
animal.eat;
}
}
}

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

所以,泛型內是不存在父子關係,但是利用通配符可以產生類似的效果:

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

  • G 是 G的子類型(如List 是 List的子類型)。
  • G 是 G的子類型(如List 是 List的子類型)
  • G 與 G等同,如List 與List等同

三: 泛型可以用到那些地方

泛型可以用到容器,方法,介面,內部類,抽象類

四: Java中泛型獨特之處

泛型是Java1.5之後才引入的,為了兼容。Java採用了C++完全不同的實現思想。Java中的泛型更多的看起來像是編譯期用的,比如我定義一個使用泛型的demo

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

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

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

擦除的原理以及邊界

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

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

泛型擦除肯可能導致的問題用泛型不可以區分方法簽名

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

泛型類的靜態變數是共享

public class StaticTest{
public static void main(String[] args){
GT gti = new GT;
gti.var=1;
GT gts = new GT;
gts.var=2;
System.out.println(gti.var);
}
}
class GT{
public static int var=0;
public void nothing(T x){}
}

五: 泛型中特殊使用

java中的泛型不只是上述說的內容,還有一些特殊的地方,如果這些地方也用泛型該怎麼設計。比如說「動態類型」,「潛在類型」,「異常」

程序如果運行時需要類型信息

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

異常中使用泛型

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

數組與泛型

不能聲明參數化類型的數組, 數組可以記住自己的元素類型,不能建立一個泛型數組。(當然 你如果用反射還是可以創建的,用Array.newInstance。這裡說不能建是不能用普通方法)

泛型的一些其他細節:

1.基本類型無法作為類型參數即ArrayList這樣的代碼是不允許的,如果為我們想要使用必須使用基本類型對應的包裝器類型ArrayList

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

3.注,在能夠使用泛型方法的時候,盡量避免使整個類泛化。

六:簡單概括

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

所有泛型類的類型參數在編譯時都會被擦除

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

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

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

EasyNetQ之多態發布和訂閱
SQL執行過程中的性能負載點
web本地存儲技術-cookie、sessionStorage和localStorage
常見的排序演算法總結(JavaScript)

TAG:科技優家 |

您可能感興趣

Mock API是如何在開發中發光發熱的?
微軟員工證實新版的HoloLens正在開發中
研發實戰:現在開始,用Unity為Magic Leap One開發MR內容
微軟首次確認《戰爭機器5》在開發中 首發將加入Xbox Game Pass
開發者從代碼中發現一款配有Face ID的iPad Pro
蘋果:開發者不得在應用中亂加emoji表情
微軟允許開發者在Microsoft Store中向私人發布應用或遊戲
Limitless加入Lytro 開發遊戲引擎中結合光場和實時渲染工具
Android簡單應用開發實戰
使用谷歌的Flutter進行真正的跨平台移動開發
《Superhot VR》開發商:正在開發一款硬核VR體驗
Trivver啟動代幣銷售以鼓勵擴展現實技術的使用和開發
Unreal Studio,一個提供實驗、自由、按需開發的VR可視化平台
微軟確認新款HoloLens正在開發中
開發商Wargaming正在為VR街機開發全新《坦克世界 VR》
Google 正為中國市場開發定製版新聞聚合應用?
Uplay,Playu,聊聊著名的旅遊模擬器開發大廠,育碧的發家史
淺談HTTP在WebApi開發中的運用
Facebook讓開發者創建與物理對象相關的AR內容
大作「批發商」Devolver:開發者關心的不該是銷量、而是品質