一個你讀框架源碼之前必須要理解的技術點,泛型(一)

泛型

泛型(Generic)是Java編程語言的強大功能。
泛型的本質是參數化類型,在不創建新的類型的情況下,通過泛型指定某一個類、方法或接口成不同類型。將類型由原來的具體類參數化,此時的參數可以稱爲類型形參

public class Test1 {
	/**
	 * @param args
	 * ArrayList<Integer>,ArrayList被指定了一個Integer的類型形參,所以此時的ArrayList只能添加Integer的對象
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(111);
		list.add(222);
		System.out.println(list);
	}
}

泛型的優點

1.在編譯時進行更強的類型檢查。Java編譯器將強類型檢查應用於通用代碼,如果代碼違反類型安
全,則會發出錯誤。修復編譯時錯誤比修復運行時錯誤容易,後者可能很難找到。
2.消除類型轉換
3.可以幫助我們寫出更加通用的代碼

泛型擦除

java中的泛型 只是在編譯期做語法檢查 對你進行限制
泛型只在編譯期有效(class).運行期沒有泛型,泛型就被擦除了,所有類型都變成了相同的基本類型

public class Test1 {

	/**
	 * @param args
	 * ArrayList<Integer>的一個對象,在這個集合中添加一個字符串數據,如何實現呢?
	 * 泛型只在編譯期有效,在運行期會被擦除掉
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(111);
		list.add(222);
		Class clazz = Class.forName("java.util.ArrayList");				//獲取字節碼對象
		Method m = clazz.getMethod("add", Object.class);				//獲取add方法,add方法中可以放任意對象所以參數是Object.class
		m.invoke(list, "abc");
		System.out.println(list);
	}
}

泛型類

泛型類型修飾類爲泛型類,例如List
1.泛型類可以由多個泛型參數例如Class<T,R>
2.泛型參數如果想使用基本數據類型的話必須傳遞其包裝類,舉例:int->Integer
3.如果不給泛型類指定具體類型,此時泛型類型可以爲任意類型

public class ExampleUnitTest {
    @Test
    public void exampleTest() {
        TestClass<Integer> object1 = new TestClass<>(1);//泛型是Integer類型,int的包裝類
        TestClass<String> object2 = new TestClass<>("2");//泛型是String類型
        TestClass object3 = new TestClass();//如果不給泛型類指定具體類型,類中使用泛型的成員可以爲任意類型
        object3.setMember(123);
        System.out.println(object3.member);
        object3.setMember("123");//
        System.out.println("exampleTest: " + object1.getMember() + " , " + object2.getMember() + " , " + object3.getMember());
    }

    public class TestClass<T> {
        private T member;

        public TestClass() {

        }

        public TestClass(T member) {
            this.member = member;
        }

        public T getMember() {
            return member;
        }

        public void setMember(T member) {
            this.member = member;
        }
    }
}

輸出

123
exampleTest: 1 , 2 , 123

泛型接口

泛型修飾接口
泛型接口的實現類未傳入具體的泛型實參,此時應該將泛型同樣聲明到類中,否者使用的時候無法確定具體類型

public interface TestInterface1<R> {
    void callback(R r);
}
public class ExampleClass4<R> implements TestInterface1<R> {
    //泛型接口的實現類未傳入具體的泛型,舉例<T>,此時應該將泛型同樣聲明到類中
    @Override
    public void callback(R r) {
        System.out.println(r);
    }
}
public class ExampleUnitTest {
    @Test
    public void exampleTest2(){
        ExampleClass4<String> exampleClass4 = new ExampleClass4<>();
        exampleClass4.callback("String類型");
    }
}

打印

String類型

泛型接口的實現類傳入具體的泛型實參,舉例 < String>,使用對應泛型的地方都應該爲對應類型

public class ExampleClass5 implements TestInterface1<String> {
    @Override
    public void callback(String s) {
        System.out.println(s);
    }
}
public class ExampleUnitTest {
    @Test
    public void exampleTest2(){
        ExampleClass5 exampleClass5 = new ExampleClass5();
        exampleClass5.callback("exampleClass5");
    }

}

打印

exampleClass5

如果泛型接口有兩個泛型參數的時候,當泛型接口的實現類傳入了具體的泛型實參,情況和一個泛型參數的一樣,使用對應泛型的參數或成員都應該爲對應類型
這裏注意的是當泛型接口的實現類沒有傳入具體的泛型實參的情況

泛型接口的實現類未傳入具體的泛型,舉例<T,R>,此時應該將泛型同樣聲明到類中
alt+enter默認會在ExampleClass2聲明第一個泛型T,如果想要使用泛型R,也需要在ExampleClass2類上聲名,否者會報錯,見以下例子

public interface TestInterface2<T, R> {
    void callback(T t);
    R onNext();
}
public class ExampleClass2<T> implements TestInterface2<T, R> {
    //泛型接口的實現類未傳入具體的泛型,舉例<T,R>,此時應該將泛型同樣聲明到類中
    //alt+enter默認會在ExampleClass2聲明第一個泛型T,如果想要使用泛型R,也需要在ExampleClass2類上聲名

    private R member;

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

    @Override
    public R onNext() {
        return getMember();
    }

    private R getMember() {
        return member;
    }

    public void setMember(R member) {
        this.member = member;
    }

}

具體使用的時候會報錯cannot be applied to …
在這裏插入圖片描述
所以如果外部需要使用到第二個泛型參數,同樣也需要聲明到類上

public class ExampleClass3<T,R> implements TestInterface2<T, R> {
    //泛型接口的實現類未傳入具體的泛型,舉例<T,R>,此時應該將泛型同樣聲明到類中
    private R member;

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

    @Override
    public R onNext() {
        return getMember();
    }

    private R getMember() {
        return member;
    }

    public void setMember(R member) {
        this.member = member;
    }

}
public class ExampleUnitTest {
    @Test
    public void exampleTest2(){
        ExampleClass3<String, Boolean> exampleClass3 = new ExampleClass3<>();
        exampleClass3.callback("傳遞String");
        exampleClass3.setMember(true);
        Boolean aBoolean = exampleClass3.onNext();
    }
}

泛型方法

1.在訪問權限和返回值類型之間聲明方法的泛型,注意,這個和泛型類 TestMethod的T不是同一個,可以是相同類型也可以是不同類型,當然中的T可以換成任意字母
2.聲明瞭泛型的方法纔是泛型方法,僅在方法中,沒有聲明的不是泛型方法,比如當前類的構造函數public TestMethod(T t){},就不是泛型方法
3.泛型方法中聲明的泛型,可以在方法中使用該泛型,反之不能使用該泛型
例子:

public class TestMethod<T> {
    private T mT;
    public TestMethod(T t) {
        this.mT = t;
    }

    public T getmT() {
        return mT;
    }

    /**
     * 泛型方法
     * 1.在訪問權限和返回值類型之間聲明方法的泛型<T>,注意,這個<T>和泛型類 TestMethod<T>的T不是同一個,可以是相同類型也可以是不同類型,取決於泛型實參傳的是什麼類型,可以見例子ExampleUnitTest,當然<T>中的T可以換成任意字母
     * 2.聲明瞭泛型<T>的方法纔是泛型方法,僅在方法中,沒有聲明的不是泛型方法,比如當前類的構造函數public TestMethod(T t){},就不是泛型方法
     * 3.泛型方法中聲明的泛型<T>,可以在方法中使用該泛型,反之不能使用該泛型
     * @param t
     * @param <T>
     */
    public <T> void myMethod1(TestMethod<T> t){
        T t1 = t.getmT();
        System.out.println(t1+","+t1.getClass().toString());
    }

    /**
     * 不是泛型方法,只是在參數中使用了類中聲明的泛型T,此處如果調用該方法,只能傳和類一致的泛型
     * @param t
     */
    public void myMethod2(TestMethod<T> t){
        T t1 = t.getmT();
        System.out.println(t1+","+t1.getClass().toString());
    }
}
public class ExampleUnitTest {
    @Test
    public void test() {
        TestMethod<String> testMethod1 = new TestMethod<>("泛型方法");
        TestMethod<Integer> testMethod2 = new TestMethod<>(123);
        /**
         * 下面方法調用正常
         * 因爲在方法中聲明瞭新泛型,可以在方法中使用
         */
        testMethod1.myMethod1(testMethod1);
        /**
         * 下面報錯:TestMethod<String> in TestMethod cannot be applied to TestMethod<Integer>
         * 因爲myMethod2使用的類中聲明的泛型,當前是String類型的,而傳入Integer,所以報錯
         */
        testMethod1.myMethod2(testMethod2);
    }

}

註釋掉testMethod1.myMethod2(testMethod2);,運行

泛型方法,class java.lang.String

注意:如果在靜態方法中使用泛型,這個靜態方法必須設置成靜態方法

泛型通配符

<?>

可以傳入任意類型,例如在上一節泛型方法中舉的例子,在myMethod2中使用的是類中的泛型,對象testMethod1對應類中的泛型是String,而想傳入泛型是Integer的對象,在不是泛型方法的情況下是行不通的,這裏如果我們在方法參數上使用泛型通配符,就可以解決這個問題
例子:

    /**
     * 泛型通配符,可以傳入任意類型
     * @param t
     */
    public void myMethod3(TestMethod<?> t){
        Object o = t.getmT();
        System.out.println(o+" , "+o.getClass().toString());
    }
    @Test
    public void test() {
        TestMethod<String> testMethod1 = new TestMethod<>("泛型方法");
        TestMethod<Integer> testMethod2 = new TestMethod<>(123);
        /**
         * 下面方法調用正常
         * 因爲在方法中聲明瞭新泛型,可以在方法中使用
         */
//        testMethod1.myMethod1(testMethod1);
        /**
         * 下面報錯:TestMethod<String> in TestMethod cannot be applied to TestMethod<Integer>
         * 因爲myMethod2使用的類中聲明的泛型,當前是String類型的,而傳入Integer,所以報錯
         */
//        testMethod1.myMethod2(testMethod2);
        testMethod1.myMethod3(testMethod2);
    }

輸出

123 , class java.lang.Integer

泛型上下邊界

在使用泛型的時候,可以限制泛型實參的上下邊界,也就是:

指定泛型的父類是某種類型就是限制泛型的上邊界,只能使用上邊界類型和上邊界內的類型,例如< ? extends TestClass1>,上邊界是TestClass1,可以使用泛型TestClass1和TestClass1的子類泛型;

指定泛型的子類是某種類型就是限制泛型的下邊界,只能使用下邊界類型的泛型和下邊界以內的泛型,例如< ? super TestClass2 > 下邊界是TestClass2,只能使用泛型爲TestClass2和TestClass2的父類泛型

舉例
水果、蘋果、綠蘋果的例子,水果是超類,蘋果繼承水果,綠蘋果繼承蘋果;
定義一個泛型容器Generic,
eat2(Generic<? extends Apple> t) 方法是確定了上邊界爲Apple,所以只能傳的泛型爲Apple或者GreenApple
eat3(Generic<? super Apple> t) 方法是確定了下邊界爲Apple,所以只能傳的泛型爲Apple或者Friut

public class Fruit {
    private String name;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Apple extends Fruit {
    public Apple() {
    }

    public Apple(String name) {
        super(name);
    }

    @Override
    public String toString() {
        return "Apple{}";
    }
}
public class GreenApple extends Apple {
    public GreenApple() {
    }

    public GreenApple(String name) {
        super(name);
    }

    @Override
    public String toString() {
        return "GreenApple{}";
    }
}
public class Generic<T> {
    private T t;

    public Generic() {
    }

    public Generic(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }


    public String eat2(Generic<? extends Apple> t){

        return t.getT().toString();
    }

    public String eat3(Generic<? super Apple> t){

        return t.getT().toString();
    }
}
@Test
    public void exampleTest3() {
        Generic<Fruit> fruitGeneric = new Generic<>();
        Generic<Apple> appleGeneric = new Generic<>();
        Generic<GreenApple> greenAppleGeneric = new Generic<>();
        Fruit fruit = new Fruit("水果");
        Apple apple = new Apple("蘋果");
        GreenApple greenApple = new GreenApple("綠蘋果");
        fruitGeneric.setT(fruit);
        appleGeneric.setT(apple);
        greenAppleGeneric.setT(greenApple);

        //Apple是上邊界 可以傳Generic中泛型繼承於Apple的子類以及Apple
        System.out.println(appleGeneric.eat2(greenAppleGeneric));
        System.out.println(appleGeneric.eat2(appleGeneric));
        
        //Apple是下邊界 可以傳Generic中Apple泛型的父類以及Apple
        System.out.println(appleGeneric.eat3(fruitGeneric));
        System.out.println(appleGeneric.eat3(appleGeneric));
    }

打印

GreenApple{}
Apple{}
com.himi.test.Fruit@3b95a09c
Apple{}

結束語

先寫一下泛型基本使用,過些日子寫一下其它用法和注意事項

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章