本篇內(nèi)容介紹了“Java泛型的概念”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計、成都做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、太倉ssl等。為近1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的太倉網(wǎng)站制作公司
所謂泛型,就是允許在定義類、接口時通過一個標識表示類中某個屬性的類型或者是某個方法的返 回值及參數(shù)類型。這個類型參數(shù)將在使用時(例如,繼承或?qū)崿F(xiàn)這個接口,用這個類型聲明變量、 創(chuàng)建對象時確定(即傳入實際的類型參數(shù),也稱為類型實參)。
從JDK 5.0以后,Java引入了“參數(shù)化類型(Parameterized type)”的概念,允許我們在創(chuàng)建集合時再指定集合元素的類型,正如:List<String>,這表明該List只能保存字符串類型的對象。
JDK 5.0改寫了集合框架中的全部接口和類,為這些接口、類增加了泛型支持,從而可以在聲明集合變量、創(chuàng)建集合對象時傳入類型實參。
集合容器類在設(shè)計階段/聲明階段不能確定這個容器到底實際存的是什么類型的對象,所以在JDK1.5之前只能把元素類型設(shè)計為Object,JDK1.5之后使用泛型來解決。因為這個時候除了元素的類型不確定,其他的部分是確定的,例如關(guān)于這個元素如何保存,如何管理等是確定的,因此此時把元素的類型設(shè)計成一個參數(shù),這個類型參數(shù)叫做泛型。Collection<E>,List<E>,ArrayList<E> 這個<E>就是類型參數(shù),即泛型。
解決元素存儲的安全性問題,好比商品、藥品標簽,不會弄錯。
解決獲取數(shù)據(jù)元素時,需要類型強制轉(zhuǎn)換的問題,好比不用每回拿商品、藥品都要辨別。
Java泛型可以保證如果程序在編譯時沒有發(fā)岀警告,運行時就不會產(chǎn)生ClassCastException異常。同時,代碼更加簡潔、健壯。
@Test public void test1(){ ArrayList list = new ArrayList(); //需求:存放學(xué)生的成績 list.add(78); list.add(76); list.add(89); list.add(88); //問題一:類型不安全 // list.add("Tom"); for(Object score : list){ //問題二:強轉(zhuǎn)時,可能出現(xiàn)ClassCastException int stuScore = (Integer) score; System.out.println(stuScore); } }
圖示:
//在集合中使用泛型,以ArrayList為例 @Test public void test1(){ ArrayList<String> list = new ArrayList<>(); list.add("AAA"); list.add("BBB"); list.add("FFF"); list.add("EEE"); list.add("CCC"); //遍歷方式一: Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("-------------"); //便利方式二: for (String str: list) { System.out.println(str); } }
圖示:
@Test //在集合中使用泛型的情況:以HashMap為例 public void test2(){ Map<String,Integer> map = new HashMap<>();//jdk7新特性:類型推斷 map.put("Tom",26); map.put("Jarry",30); map.put("Bruce",28); map.put("Davie",60); //嵌套循環(huán) Set<Map.Entry<String, Integer>> entries = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); while (iterator.hasNext()){ Map.Entry<String, Integer> entry = iterator.next(); String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } }
① 集合接口或集合類在JDK 5.0時都修改為帶泛型的結(jié)構(gòu)。
② 在實例化集合類時,可以指明具體的泛型類型
③ 指明完以后,在集合類或接口中凡是定義類或接口時,內(nèi)部結(jié)構(gòu)(比如:方法、構(gòu)造器、屬性等)使用到類的泛型的位置,都指定為實例化的泛型類型。
比如:add(E e) --->實例化以后:add(Integer e)
④ 注意點:泛型的類型必須是類,不能是基本數(shù)據(jù)類型。需要用到基本數(shù)據(jù)類型的位置,拿包裝類替換
⑤ 如果實例化時,沒有指明泛型的類型。默認類型為java.lang.Object類型。
泛型類、泛型接口、泛型方法
interface List<T> 和 class GenTest<K,V>其中,T,K,V,不代表值,而是表示類型。這里使用任意字母都可以。
常用T表示,是Type的縮寫。
一定要在類名后面指定類型參數(shù)的值(類型)。如:
List<String> strList =new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
T只能是類,不能用基本數(shù)據(jù)類型填充。但可以使用包裝類填充
把一個集合中的內(nèi)容限制為一個特定的數(shù)據(jù)類型,這就是 generics背后的核心思想
//JDK 5.0以前 Comparable c = new Date(); System.out.println(c.comparaTo("red"); //JDK 5.0以后 Comparable <Date> c = new Date(); System.out.println(c.comparaTo("red");
總結(jié):使用泛型的主要優(yōu)點在于能夠在編譯時而不是在運行時檢測錯誤
泛型類可能有多個參數(shù),此時應(yīng)將多個參數(shù)一起放在尖括號內(nèi)。比如<E1,E2,E3>
泛型類的構(gòu)造器如下: public GenericClass(){}
而下面是錯誤的: public GenericClass<E>{}
實例化后,操作原來泛型位置的結(jié)構(gòu)必須與指定的泛型類型一致。
泛型不同的引用不能相互賦值。
盡管在編譯時 ArrayList<String>和ArrayList<Integer>是兩種類型,但是,在運行時只有一個ArrayList被加載到JVM中。
泛型如果不指定,將被擦除,泛型對應(yīng)的類型均按照Object處理,但不等價于Object。
建議:泛型要使用一路都用。要不用,一路都不要用。
如果泛型結(jié)構(gòu)是一個接口或抽象類,則不可創(chuàng)建泛型類的對象。
JDK 7.0,泛型的簡化操作: ArrayList<Fruit>first= new ArrayList<>();(類型推斷)
泛型的指定中不能使用基本數(shù)據(jù)類型,可以使用包裝類替換。
在類/接口上聲明的泛型,在本類或本接口中即代表某種類型,可以作為非靜態(tài)屬性的類型、非靜態(tài)方法的參數(shù)類型、非靜態(tài)方法的返回值類型。但在靜態(tài)方法中不能使用類的泛型。
異常類不能是泛型的。
不能使用new E[]。但是可以:E[] elements= (E[])new Object[capacity];
> 參考:ArrayList源碼中聲明:Object\[\] elementData,而非泛型參數(shù)類型數(shù)組。
父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型類型:
- 子類不保留父類的泛型:按需實現(xiàn) - 沒有類型---擦除 - 具體類型 - 子類保留父類的泛型:泛型子類 - 全部保留 - 部分保留 - 結(jié)論:子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自己的泛型
代碼示例:
class Father<T1, T2> { } /** * 定義泛型子類Son * 情況一:繼承泛型父類后不保留父類的泛型 */ //1.沒有指明類型 擦除 class Son1<A, B> extends Father {//等價于class Son1 extends Father<Object,Odject>{} } //2.指定具體類型 class Son2<A, B> extends Father<Integer, String> { } /** * 定義泛型子類Son * 情況二:繼承泛型父類后保留泛型類型 */ //1.全部保留 class Son3<T1, T2, A, B> extends Father<T1, T2> { } //2.部分保留 class Son4<T2, A, B> extends Father<Integer,T2>{ }
代碼示例:
/** * 自定義泛型類Order */ class Order<T> { private String orderName; private int orderId; //使用T類型定義變量 private T orderT; public Order() { } //使用T類型定義構(gòu)造器 public Order(String orderName, int orderId, T orderT) { this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } //這個不是泛型方法 public T getOrderT() { return orderT; } //這個不是泛型方法 public void setOrderT(T orderT) { this.orderT = orderT; } //這個不是泛型方法 @Override public String toString() { return "Order{" + "orderName='" + orderName + '\'' + ", orderId=" + orderId + ", orderT=" + orderT + '}'; } // //靜態(tài)方法中不能使用類的泛型。 // public static void show(T orderT){ // System.out.println(orderT); // } // //try-catch中不能是泛型的。 // public void show(){ // try { // // }catch (T t){ // // } // } //泛型方法:在方法中出現(xiàn)了泛型的結(jié)構(gòu),泛型參數(shù)與類的泛型參數(shù)沒有任何關(guān)系。 //換句話說,泛型方法所屬的類是不是泛型類都沒有關(guān)系。 //泛型方法,可以聲明為靜態(tài)的。 // 原因:泛型參數(shù)是在調(diào)用方法時確定的。并非在實例化類時確定。 public static <E> List<E> copyFromArryToList(E[] arr) { ArrayList<E> list = new ArrayList<>(); for (E e : list) { list.add(e); } return list; } }
自定義泛型類Order的使用
@Test public void test1() { //如果定義了泛型類,實例化沒有指明類的泛型,則認為此泛型類型為Object類型 //要求:如果大家定義了類是帶泛型的,建議在實例化時要指明類的泛型。 Order order = new Order(); order.setOrderT(123); System.out.println(order.getOrderT()); order.setOrderT("abc"); System.out.println(order.getOrderT()); //建議:實例化時指明類的泛型 Order<String> order1 = new Order<>("Tom", 16, "male"); order1.setOrderT("AA:BBB"); System.out.println(order1.getOrderT()); } @Test //調(diào)用泛型方法 public void test2(){ Order<String> order = new Order<>(); Integer [] arr = new Integer[]{1,2,3,4,5,6}; List<Integer> list = order.copyFromArryToList(arr); System.out.println(list); }
代碼示例:
/** * 自定義泛型接口 */ public interface DemoInterface <T> { void show(); int size(); } //實現(xiàn)泛型接口 public class Demo implements DemoInterface { @Override public void show() { System.out.println("hello"); } @Override public int size() { return 0; } } @Test //測試泛型接口 public void test3(){ Demo demo = new Demo(); demo.show(); }
方法,也可以被泛型化,不管此時定義在其中的類是不是泛型類。在泛型方法中可以定義泛型參數(shù),此時,參數(shù)的類型就是傳入數(shù)據(jù)的類型。
泛型方法的格式: [訪問權(quán)限]<泛型>返回類型 方法名(泛型標識 參數(shù)名稱])拋出的異常
泛型方法聲明泛型時也可以指定上限
代碼示例:
//泛型方法:在方法中出現(xiàn)了泛型的結(jié)構(gòu),泛型參數(shù)與類的泛型參數(shù)沒有任何關(guān)系。 //換句話說,泛型方法所屬的類是不是泛型類都沒有關(guān)系。 //泛型方法,可以聲明為靜態(tài)的。 // 原因:泛型參數(shù)是在調(diào)用方法時確定的。并非在實例化類時確定。 public static <E> List<E> copyFromArryToList(E[] arr) { ArrayList<E> list = new ArrayList<>(); for (E e : list) { list.add(e); } return list; }
泛型實際上就是標簽,聲明時不知道類型,再使用時指明
定義泛型結(jié)構(gòu),即:泛型類、接口、方法、構(gòu)造器時貼上泛型的標簽<T>
用泛型定義類或借口是<T>放到類名或接口名后面,定義泛型方法時在方法名前加上<T>
【DAO.java】:定義了操作數(shù)據(jù)庫中的表的通用操作。 ORM思想(數(shù)據(jù)庫中的表和Java中的類對應(yīng))
public class DAO<T> {//表的共性操作的DAO //添加一條記錄 public void add(T t){ } //刪除一條記錄 public boolean remove(int index){ return false; } //修改一條記錄 public void update(int index,T t){ } //查詢一條記錄 public T getIndex(int index){ return null; } //查詢多條記錄 public List<T> getForList(int index){ return null; } //泛型方法 //舉例:獲取表中一共有多少條記錄?獲取最大的員工入職時間? public <E> E getValue(){ return null; } }
【CustomerDAO.java】:
public class CustomerDAO extends DAO<Customer>{//只能操作某一個表的DAO }
【StudentDAO.java】:
public class StudentDAO extends DAO<Student> {//只能操作某一個表的DAO }
泛型在繼承方面的體現(xiàn):
雖然類A是類B的父類,但是G<A> 和G<B>二者不具備子父類關(guān)系,二者是并列關(guān)系。
補充:類A是類B的父類,A<G> 是 B<G> 的父類
代碼示例:
@Test public void test1(){ Object obj = null; String str = null; obj = str; Object[] arr1 = null; String[] arr2 = null; arr1 = arr2; //編譯不通過 // Date date = new Date(); // str = date; List<Object> list1 = null; List<String> list2 = new ArrayList<String>(); //此時的list1和list2的類型不具子父類關(guān)系 //編譯不通過 // list1 = list2; /* 反證法: 假設(shè)list1 = list2; list1.add(123);導(dǎo)致混入非String的數(shù)據(jù)。出錯。 */ show(list1); show1(list2); } public void show1(List<String> list){ } public void show(List<Object> list){ } @Test public void test2(){ AbstractList<String> list1 = null; List<String> list2 = null; ArrayList<String> list3 = null; list1 = list3; list2 = list3; List<String> list4 = new ArrayList<>(); }
使用類型通配符:?
比如:List<?>,Map<?,?>
List<?>是List<String>、List<Object>等各種泛型List的父類。
讀取List<?>的對象list中的元素時,永遠是安全的,因為不管list的真實類型是什么,它包含的都是Object
寫入list中的元素時,不可以。因為我們不知道c的元素類型,我們不能向其中添加對象。 除了添加null之外。
說明:
將任意元素加入到其中不是類型安全的
Collection<?> c = new ArrayList<String>()
c.add(new Object());//編譯時錯誤
因為我們不知道c的元素類型,我們不能向其中添加對象。add方法有類型參數(shù)E作為集合的元素類型。我們傳給add的任何參數(shù)都必須是一個已知類型的子類。因為我們不知道那是什么類型,所以我們無法傳任何東西進去。
唯一的例外的是null,它是所有類型的成員。
我們可以調(diào)用get()方法并使用其返回值。返回值是一個未知的類型,但是我們知道,它總是一個Object。
代碼示例:
@Test public void test3(){ List<Object> list1 = null; List<String> list2 = null; List<?> list = null; list = list1; list = list2; //編譯通過 // print(list1); // print(list2); // List<String> list3 = new ArrayList<>(); list3.add("AA"); list3.add("BB"); list3.add("CC"); list = list3; //添加(寫入):對于List<?>就不能向其內(nèi)部添加數(shù)據(jù)。 //除了添加null之外。 // list.add("DD"); // list.add('?'); list.add(null); //獲取(讀取):允許讀取數(shù)據(jù),讀取的數(shù)據(jù)類型為Object。 Object o = list.get(0); System.out.println(o); } public void print(List<?> list){ Iterator<?> iterator = list.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } }
//注意點1:編譯錯誤:不能用在泛型方法聲明上,返回值類型前面<>不能使用? public static <?> void test(ArrayList<?> list){ } //注意點2:編譯錯誤:不能用在泛型類的聲明上 class GenericTypeClass<?>{ } //注意點3:編譯錯誤:不能用在創(chuàng)建對象上,右邊屬于創(chuàng)建集合對象 ArrayList<> list2 new ArrayList<?>();
<?>:允許所有泛型的引用調(diào)用
通配符指定上限
上限extends:使用時指定的類型必須是繼承某個類,或者實現(xiàn)某個接口,即<=
通配符指定下限
下限super:使用時指定的類型不能小于操作的類,即>=
舉例:
<?extends Number>(無窮小, Number\] 只允許泛型為Number及Number子類的引用調(diào)用
<?super Number>\[Number,無窮大) 只允許泛型為Number及Number父類的引用調(diào)用
<? extends Comparable> 只允許泛型為實現(xiàn) Comparable接口的實現(xiàn)類的引用調(diào)用
代碼示例:
@Test public void test4(){ List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = new ArrayList<Student>(); List<Person> list4 = new ArrayList<Person>(); List<Object> list5 = new ArrayList<Object>(); list1 = list3; list1 = list4; // list1 = list5; // list2 = list3; list2 = list4; list2 = list5; //讀取數(shù)據(jù): list1 = list3; Person p = list1.get(0); //編譯不通過 //Student s = list1.get(0); list2 = list4; Object obj = list2.get(0); ////編譯不通過 // Person obj = list2.get(0); //寫入數(shù)據(jù): //編譯不通過 // list1.add(new Student()); //編譯通過 list2.add(new Person()); list2.add(new Student()); }
“Java泛型的概念”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
文章題目:Java泛型的概念
標題URL:http://chinadenli.net/article22/gicdcc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、網(wǎng)站內(nèi)鏈、品牌網(wǎng)站建設(shè)、外貿(mào)建站、標簽優(yōu)化、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)