1. 程式人生 > >Java SE面向物件--13.Java泛型

Java SE面向物件--13.Java泛型

1、概述

在前面學習集合時,我們都知道集合中是可以存放任意物件的,只要把物件儲存集合後,那麼這時他們都會被提升成Object型別。當我們在取出每一個物件,並且進行相應的操作,這時必須採用型別轉換。

public class GenericDemo {
	public static void main(String[] args) {
		Collection coll = new ArrayList();
		coll.add("abc");
		coll.add("itcast");
		coll.add(5);//由於集合沒有做任何限定,任何型別都可以給其中存放
		Iterator it = coll.iterator();
		while(it.hasNext()){
			//需要列印每個字串的長度,就要把迭代出來的物件轉成String型別
			String str = (String) it.next();
			System.out.println(str.length());
		}
	}
}

程式在執行時發生了問題java.lang.ClassCastException。怎麼解決這個問題 呢、下面就來說說泛型

泛型可以在類或方法中預支地使用未知的型別。用來靈活地將資料型別應用到不同的類、方法、介面當中。將資料型別作為引數進行傳遞。

好處:

  • 將執行時期的ClassCastException,轉移到了編譯時期變成了編譯失敗。

  • 避免了型別強轉的麻煩。

2、定義泛型類

格式:

修飾符 class 類名<代表泛型的變數> {  }
// 自定義一個泛型類, 泛型的標誌是 <>
// E (Element)  T (Type)  K (Key)  V (Value)  R (Result)

// 請問 : 泛型類中的泛型在何時進行確定 ??? 在例項化該物件時, 進行泛型真實型別的確定.
// 泛型類中泛型是為本類的屬性和方法提供的.  ArrayList<E>
public class GenericClass1<T> {

    // 屬性
    private T field;

    // 方法 :
    public void setField(T field) {
        this.field = field;
    }

    public T getField() {
        return field;
    }
}

演示程式碼:

public class TestGenericClass1 {
    public static void main(String[] args) {

        // 1. 建立一個泛型類物件
        GenericClass1<String> g1 = new GenericClass1<>();

        g1.setField("hello Java.");
        String str = g1.getField();
        System.out.println(str);


        GenericClass1<Integer> g2 = new GenericClass1<>();

        g2.setField(998);
        Integer number = g2.getField();
        System.out.println(number);
    }
}

3、定義泛型介面

格式:

修飾符 interface介面名<代表泛型的變數> {  }
// 定義了一個泛型介面 : 類可以在例項化同時確定類上的泛型, 而介面不能例項化, 何時確定泛型呢 ??? 實現類來確定.

public interface MyGenericInterface<T> {

    // 抽象方法
    void method(T t);
}

演示程式碼:

介面程式碼一:

// 確定介面中泛型的第一種方式, 實現類在實現該介面的同時進行確定.

public class MyGenericInterfaceImpl1 implements MyGenericInterface<String> {

    @Override
    public void method(String s) {
   
        System.out.println(s);
    }
}

介面程式碼二:

// 確定介面中泛型的第二種方式, 當實現類實現該介面時, 不確定該介面的泛型, 此時, 該類必須定義為 `泛型類`.
// 泛型類在例項化物件時, 就可以確定泛型的真實型別, 此時, 該型別就可以被傳遞介面的泛型中, 從而來確定介面的泛型.

public class MyGenericInterfaceImpl2<T> implements MyGenericInterface<T> {

    @Override
    public void method(T t) {
        System.out.println(t);
    }
}

測試類:

public class TestMyGenericInterface {
    public static void main(String[] args) {

        // 1. 實現類 MyGenericInterfaceImpl1
        MyGenericInterfaceImpl1 g1 = new MyGenericInterfaceImpl1();
        g1.method("你好, 上海.");

        // ArrayList<E> 實現了 List<E> 介面. 

        // 2. 實現類 MyGenericInterfaceImpl2
        MyGenericInterfaceImpl2<String> g2 = new MyGenericInterfaceImpl2<>();
        g2.method("hello ShangHai.");

        MyGenericInterfaceImpl2<Integer> g3 = new MyGenericInterfaceImpl2<>();
        g3.method(998);
    }
}

4、定義泛型方法

格式:

修飾符 <代表泛型的變數> 返回值型別 方法名(引數){  }

程式碼:

public class GenericMethodTest {
    public static void main(String[] args) {

        printInfo(new Person("張三", 18));

        printInfo(new Animal("哮天犬", 38));
    }

    // 定義一個方法 : 輸出 `物件資訊`  (Person, Animal)  -> Object
    // 泛型必須先定義, 後使用.  泛型使用 <> 尖括號定義.  位置: 修飾符 <T> 返回值型別 方法名(引數列表) { 方法體 }
    public static <T> void printInfo(T t) {
        System.out.println(t);
    }
}

5、泛型萬用字元:?

說明:當使用泛型類或者介面時,傳遞的資料中,泛型型別不確定,可以通過萬用字元<?>表示。但是一旦使用泛型的萬用字元後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。

使用場景 : 必須在已經定義的介面或類上的泛型位置才能使用.

演示:

public class GenericMethodTest {
    public static void main(String[] args) {

        ArrayList<Person> list1 = new ArrayList<>();
        list1.add(new Person("張三", 18));
        list1.add(new Person("李四", 19));
        list1.add(new Person("王五", 20));

        printCollection(list1);


        ArrayList<Animal> list2 = new ArrayList<>();
        list2.add(new Animal("哮天犬1號", 100));
        list2.add(new Animal("哮天犬2號", 200));

        printCollection(list2);
    }

    // 需求 : 定義一個方法, 輸出集合中所有元素的資訊.
    // ? 泛型的萬用字元, 好處, 不要宣告.
    // 預設 ? extends Object
    // 翻譯 : ? 表示我不知道這是什麼型別, 但是, 我知道, 它可以是 Object 型別, 或者該型別 繼承 Object 型別.
    public static void printCollection(Collection<?> c) {
        for (Object obj : c){
            System.out.println(obj);
        }
    }

    /*public static <T> void printCollection(Collection<T> c) {
        for (T t : c) {
            System.out.println(t);
        }
    }*/
}

泛型上下限:

泛型的上限:
格式: 型別名稱 <? extends 類 > 物件名稱
意義: 只能接收該型別及其子類
泛型的下限:
格式: 型別名稱 <? super 類 > 物件名稱
意義: 只能接收該型別及其父型別

上限:

public class Test {
    public static void main(String[] args) {

        ArrayList<Person> list1 = new ArrayList<>();
        printCollection(list1);

        ArrayList<Student> list2 = new ArrayList<>();
        printCollection(list2);

        ArrayList<Teacher> list3 = new ArrayList<>();
        printCollection(list3);

        // ArrayList<Animal> list4 = new ArrayList<>();
        // printCollection(list4);
    }

    // 定義一個方法 :
    public static void printCollection(Collection<? extends Person> c) {
        for (Person p : c) {
            System.out.println(p);
        }
    }
}

下限:

public class Test {
    public static void main(String[] args) {

        ArrayList<ArmyDog> list1 = new ArrayList<>();
        printCollection(list1);

        ArrayList<Dog> list2 = new ArrayList<>();
        printCollection(list2);

        ArrayList<Animal> list3 = new ArrayList<>();
        printCollection(list3);

        /*
        ArrayList<Cat> list4 = new ArrayList<>();
        printCollection(list4);

        ArrayList<Wolf> list5 = new ArrayList<>();
        printCollection(list5);
        */
    }

    // 方法 :
    public static void printCollection(Collection<? super ArmyDog> c) {
        for (Object obj : c) {
            System.out.println(obj);
        }
    }
}

總結:

注意 : 如果泛型不指定, 那麼該泛型就會被提升為 Object 型別.

好處1 : 統一了集合中元素型別.
好處2 : 避免了型別強轉問題. 
好處3 : 將執行時期的型別轉換異常, 提前到編譯時期進行語法檢查.