【陣列】順時針列印矩陣
基本介紹
為什麼要有泛型?
所有的資料型別都可以用Object表示,也就是說可以用這個類儲存資料,那為什麼要有泛型呢?我們假設有這麼一個情況,中藥櫃子有很多的抽屜,每個抽屜就相當於一個Object,這個時候抓藥怎麼辦,每次抓藥都把抽屜開啟看一眼嗎?顯然不可能,所以就有了每個櫃子標一個標籤用來記錄這個櫃子是放啥的。這個櫃子加標籤就可以理解為這裡的泛型。所以為什麼要有泛型
- 解決元素儲存的安全性問題。(好比商品、藥品標籤,不會弄錯)
- 解決獲取資料元素時,需要型別強制轉換的問題(好比不用每回拿商品、藥品都要辨別)
Java泛型可以保證如果程式在編譯時沒有發出警告,執行時就不會產生 ClassCastException異常。同時,程式碼更加簡潔、健壯。
泛型的概念:所謂泛型,就是允許在定義類、介面時通過一個標識表示類中某個屬性的型別或者是某個方法的返
回值及引數型別。這個型別引數將在使用時(例如,繼承或實現這個介面,用這個型別宣告變數、建立物件時確定(即傳入實際的型別引數,也稱為型別實參)。
泛型的引入背景:
集合容器類在設計階段/宣告階段不能確定這個容器到底實際存的是什麼型別的物件,所以在JDK1.5之前只能把元素型別設計為Object,JDK1.5之後使用泛型來解決。因為這個時候除了元素的型別不確定,其他的部分是確定的,例如關於這個元素如何儲存,如何管理等是確定的,因此此時把元素的型別設計成一個引數,這個型別引數叫做泛型。Collection,List,ArrayList 這個就是型別引數,即泛型。
自定義泛型結構
-
泛型宣告
interface List 和 class GenTest 其中,T,K,V不代表值,而是表示型別。這裡使用任意字母都可以。常用T表示,是Type的縮寫。
-
泛型例項化
一定要在類名後面指定型別引數的值(型別)。如:
List<String> strList = new ArrayList<String>(); Iteratorr<Customer> iterator = customers.iterator();
T只能是類,不能用基本資料型別填充。但可以使用包裝類填充。把一個集合中的內容限制為一個特定的資料型別,這就是generics背後的核心思想
體會:使用泛型的主要優點是能夠在編譯時而不是在執行時檢測錯誤。
例如:
public class Order<T> {
String orderName;
//類的內部結構就可以使用類的泛型
T orderT;
//可以指定為方法的返回值
public T getOrderT(){
return orderT;
}
//方法的引數
public void setOrderT(T orderT){
this.orderT = orderT;
}
//泛型方法:在方法中出現了泛型的結構,泛型引數與類的泛型引數沒任何關係。
//換句話說,泛型方法所屬的類是不是泛型類都沒關係。
//泛型方法,可以宣告為靜態的。原因:泛型引數是在呼叫方法時確定的。並非在例項化類時確定。
public static <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
}
//建立這個物件的時候指定我們需要的資料型別即可
Order<String> o = new Order<String>();
注意:靜態方法不能使用類中的泛型(泛型只有例項化的時候由我們制定,而static類載入就載入,所以靜態方法不能使用泛型)。catch中也不能使用泛型
例如:以下兩種情況會報錯!!!
//編譯不通過
public static void show(T orderT){
System.out.println(orderT);
}
try{
}catch(T t){
}
泛型在繼承上的體現
如果B是A的一個子型別(子類或者子介面),而G是具有泛型宣告的類或介面,G並不是G的子型別! 比如:String是Object的子類,但是List並不是List 的子類。
public void testGenericAndSubClass() {
Person[] persons = null;
Man[] mans = null;
// 而 Person[] 是 Man[] 的父類.
persons = mans;
Person p = mans[0];
// 在泛型的集合上
List<Person> personList = null;
List<Man> manList = null;
// personList = manList;(報錯)
}
- 在類/介面上宣告的泛型,在本類或本介面中即代表某種型別,可以作為非靜態屬性的型別、非靜態方法的引數型別、非靜態方法的返回值型別。但在靜態方法中不能使用類的泛型。
- 異常類不能是泛型的
- 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
- 父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型型別:
- 子類不保留父類的泛型:按需實現
- 子類保留父類的泛型:泛型子類
class Father<T1, T2> {
}
// 子類不保留父類的泛型
// 1)沒有型別 擦除
class Son1 extends Father {// 等價於class Son extends Father<Object,Object>{
}
// 2)具體型別
class Son2 extends Father<Integer, String> {
}
// 子類保留父類的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {
}
====================================
class Father<T1, T2> {
}
// 子類不保留父類的泛型
// 1)沒有型別 擦除
class Son<A, B> extends Father{//等價於class Son extends Father<Object,Object>{
}
// 2)具體型別
class Son2<A, B> extends Father<Integer, String> {
}
// 子類保留父類的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}
結論:子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自己的泛型
方法,也可以被泛型化,不管此時定義在其中的類是不是泛型類。在泛型方法中可以定義泛型引數,此時,引數的型別就是傳入資料的型別。
泛型方法的格式: [訪問許可權] <泛型> 返回型別 方法名([泛型標識 引數名稱]) 丟擲的異常
例如:
public class DAO {
public <E> E get(int id, E e) {
E result = null;
return result;
}
}
泛型在集合中的使用
public void test1(){
ArrayList list = new ArrayList();
//需求:存放學生的成績
list.add(78);
list.add(76);
list.add(89);
list.add(88);
//問題一:型別不安全
//list.add("Tom");
for(Object score : list){
//問題二:強轉時,可能出現ClassCastException
int stuScore = (Integer) score;
System.out.println(stuScore);
}
}
上面這兩問題就是不使用泛型造成的,使用泛型後可寫為:
public void test2(){
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(78);
list.add(87);
list.add(99);
list.add(65);
//編譯時,就會進行型別檢查,保證資料的安全
//list.add("Tom");
//方式一:
for(Integer score : list){
//避免了強轉操作
int stuScore = score;
System.out.println(stuScore);
}
//方式二:
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
int stuScore = iterator.next();
System.out.println(stuScore);
}
}
集合中使用泛型總結:
- 集合介面或集合類在jdk5.0時都修改為帶泛型的結構。
- 在例項化集合類時,可以指明具體的泛型型別
- 指明完以後,在集合類或介面中凡是定義類或介面時,內部結構(比如:方法、構造器、屬性等)使用到類的泛型的位置,都指定為例項化的泛型型別。
- 泛型的型別必須是類,不能是基本資料型別。需要用到基本資料型別的位置,拿包裝類替換
- 如果例項化時,沒指明泛型的型別。預設型別為java.lang.Object型別。
萬用字元( ? )
類A是類B的父類,G<A>和G<B>是沒關係的,二者共同的父類是:G<?>
涉及萬用字元的集合的資料的寫入和讀取:
- 允許讀取資料,讀取的資料型別為Object。
- 新增(寫入):不能向其內部新增資料。除了新增null之外。
public void test3(){
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
//編譯通過
list = list1;
list = list2;
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
list = list3;
//新增(寫入):對於List<?>就不能向其內部新增資料。
//除了新增null之外。
// list.add("DD");
// list.add('?');
list.add(null);
//獲取(讀取):允許讀取資料,讀取的資料型別為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);
}
}
有限制條件的萬用字元的使用:
/*
限制條件的萬用字元的使用。
? extends A:
上限extends:使用時指定的型別必須是繼承某個類,或者實現某個介面,即<=
? super A:
下限super:使用時指定的型別不能小於操作的類,即>=
*/
@Test
public void test4(){
//限定這個集合只可以放Person類或者Person的子類的資料
List<? extends Person> list1 = null;
限定這個集合只可以放Person類huo'zh的資料
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;
//讀取資料:
list1 = list3;
Person p = list1.get(0);
//編譯不通過
//Student s = list1.get(0);
list2 = list4;
Object obj = list2.get(0);
//編譯不通過
//Person obj = list2.get(0);
//寫入資料:
//編譯不通過
//list1.add(new Student());
//編譯通過
list2.add(new Person());
list2.add(new Student());
}