淺談java泛型

做者:hubery, 時間:2018.11.30-12.01java

提及泛型,算是java中逼格較高的技能了吧,往往看到某個開源框架或者技術架構最頂層的接口實現,全是P/Q/R/T等的類型參數,瞬間跪着看。其實不少時候都是一葉障目,打算靜下心來研究下,寫幾個簡單的類,稍微一運行感覺下。android

期間查閱了屢次Java編程思想 這本聖經,本人理解的仍是太淺,暫且一看,附上連接。git

連接:pan.baidu.com/s/1GVwEydQp… 密碼:kijqgithub

推薦個項目:android-architecture算法

googlesample中的android-architecture項目,裏面基本都是Google的各風格的項目,值得一看,其中大量用到了泛型。編程

簡單泛型類

public class Main {
    static class Automobile {}

    public static class Holder1 {
        private Automobile a;
        public Holder1(Automobile a) { this.a = a; }
        Automobile get() { return a; }
    }

    public static class Holder2<T> {
        private T a;
        public Holder2(T a) {this.a = a;}

        public void set(T a) { this.a = a; }
        public T get() {return a;}
    }

    public static void main(String[] args) {
        Holder1 h1 = new Holder1(new Automobile());
        Holder2<Automobile> h2 = new Holder2<Automobile>(new Automobile());
    }
}
複製代碼

該測試代碼中,Holder1是普通類使用,Holder2是用了泛型T。 泛型的主要目的是:用來指定T是什麼類型,由編譯器來保證T的正確性。 咱們要的是暫時不指定類型,等用到的時候再決定是什麼類型。要達到這個效果,須要用類型參數T。用尖括號括住,放在類名後面:Holder2<T>,後續使用該類的時候用實際類型來替換此類型參數T。設計模式

泛型核心:告訴編譯器想用什麼類型,而後編譯器會幫你處理一切細節。 通常來講,泛型和其餘類型差很少,只不過泛型有類型參數而已。 使用泛型時只須要指定泛型的名稱和類型參數列表便可。bash

泛型接口

泛型可應用於接口。設計模式中工廠模式的應用:對象生成器。 如:架構

public interface Generator<T> {
    T next();
}
複製代碼

寫個生成器,next方法返回T。接口與類在使用泛型上,沒區別。框架

public class Fibonacci implements Generator<Integer> {

    private int count = 0;

    @Override
    public Integer next() {
        return fib(count++);
    }

    private int fib(int n) {
        if(n < 2) return 1;
        return fib(n - 2) + fib(n - 1);
    }

    public static void main(String[] args) {
        Fibonacci gen = new Fibonacci();
        for (int i = 0; i < 10; i++) {
            System.out.println(gen.next() + " ");
        }
    }
}
複製代碼

斐波那契數列數列的應用,實現泛型接口,指定T爲Integer型,相應next回調方法返回的T也替換成Integer型。感覺一下,完美。 至於算法方面不作多深究,這裏主要用來感覺泛型接口,找找感受。

泛型方法

泛型也能夠用在方法上。泛型方法所在的類,能夠是泛型類,也能夠是普通類,沒相關性。 泛型方法指導原則:不管什麼時候,只要你能作到,儘可能使用泛型方法。 泛型方法的定義:只需將泛型參數列表放到返回值以前便可。 看代碼:

public class GenericsMethods {
	 // 泛型方法:將參數列表寫到返回值以前就能夠了
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }

    public static void main(String[] args) {
        GenericsMethods gm = new GenericsMethods();
        gm.f("");
        gm.f(20);
        gm.f(1.1);
        gm.f(1.0F);
        gm.f(gm);
    }
}
複製代碼

打印結果:

java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
cn.hubery.generic.GenericsMethods
複製代碼

代碼中,f()擁有類型參數,由該方法的返回類型前面的類型參數列表指明的。

注:

使用泛型類時,建立對象的時候必須指定類型參數的具體類型;</p>
使用泛型方法時,沒必要指明參數類型,編譯器會自行推斷出所需的具體類型;
複製代碼

寫個通用的Generator 泛型類+泛型方法

public class BasicGenerator<T> implements Generator<T> {

    private Class<T> type;

    public BasicGenerator(Class<T> type) {
        this.type = type;
    }

    @Override
    public T next() {
        try {
          return type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
    }
}
複製代碼

寫個輔助對象類:

public class CountedObject {
    private static long counter = 0;
    private final long id = counter++;
    public long id() {
        return id;
    }

    public String toString() {
        return "CountedObject" + id;
    }
}
複製代碼

寫個測試類:

public class BasicGeneratorTest {

    public static void main(String[] args) {
        Generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(gen.next());
        }
    }
}
複製代碼

打印狀況:

CountedObject0
CountedObject1
CountedObject2
CountedObject3
CountedObject4
複製代碼

經過測試看到,用泛型方法建立Generator對象,大大減小代碼量。泛型方法的入參要求傳入Class對象,故能夠推斷出確切類型。仍是得多揣摩下。

匿名內部類

待研究。

邊界

能夠在泛型的參數類型上設置限制條件。 因爲擦除動做會移除類型信息,故用無界泛型參數調用的方法只是那些Object可用的方法。 若是將這個參數限制在某個類型的子集中,那咱們就能夠調用這些類型子集的方法。 爲了設置這種限制,java的泛型用了extends關鍵字。 此處的extends與傳統意義上的類繼承不是一回事兒。 寫法是:TestClass<T extends LineClass>

package cn.hubery.generic.line;

import java.util.List;

public class EpicBattle {

    interface SuperPower{}
    interface XRayVision extends SuperPower {
        void seeThroughWalls();
    }

    interface SuperHearing extends SuperPower {
        void hearSubtleNoises();
    }

    interface SuperSmell extends SuperPower {
        void trackBySmell();
    }

    static class SuperHero<POWER extends SuperPower> {
        POWER power;
        SuperHero(POWER power) {
            this.power = power;
        }
        POWER getPower() {
            return power;
        }
    }

    class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> {
        SuperSleuth(POWER power) {
            super(power);
        }
        void see() {
            power.seeThroughWalls();
        }
    }

    static class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> {
        CanineHero(POWER power) {
            super(power);
        }
        void hear() {
            power.hearSubtleNoises();
        }
        void smell() {
            power.trackBySmell();
        }
    }

    static class SuperHearSmell implements SuperHearing, SuperSmell {

        @Override
        public void hearSubtleNoises() {

        }

        @Override
        public void trackBySmell() {

        }
    }

    static class DogBoy extends CanineHero<SuperHearSmell> {
        DogBoy() {
            super(new SuperHearSmell());
        }
    }

    static <POWER extends SuperHearing> void useSuperHearing(SuperHero<POWER> hero) {
        hero.getPower().hearSubtleNoises();
    }

    static <POWER extends SuperHearing & SuperSmell> void superFind(SuperHero<POWER> hero) {
        hero.getPower().hearSubtleNoises();
        hero.getPower().trackBySmell();
    }

    public static void main(String[] args) {
        DogBoy dogBoy = new DogBoy();
        useSuperHearing(dogBoy);
        superFind(dogBoy);
        List<? extends SuperHearing> audioBoys;
//        List<? extends SuperHearing & SuperSmell> dogBoy;// error
    }
}
複製代碼

講真,有時候 意會不能言傳,容我回頭組織下語言再慢慢道來。

通配符

泛型參數表達式中的問號 ?

public class TPF {

    static class Fruit{}
    static class Apple extends Fruit{}

    public static void main(String[] args) {
        List<? extends Fruit> flist = Arrays.asList(new Apple());
//        flist.add(new Apple());// 不能隨意向flist中添加對象
//        flist.add(new Fruit());
        Apple a = (Apple) flist.get(0);
        flist.contains(new Apple());
        flist.indexOf(new Apple());
    }
}
複製代碼

flist類型是List<? extends Fruit>,能夠理解問:具備任何從Fruit繼承的類型的列表。但,這實際上不表明該List能夠持有任何類型的Fruit。 通配符?引用的是明確的類型,所以意味着:某種flist飲用沒有指定的具體類型。 例子還沒想清楚怎麼寫,回頭補上。

注意事項

基本類型不能做爲類型參數T

如:不能建立ArrayList之類的。若是須要,jdk會啓用自動包裝機制,int->Integer。

實現參數化接口

由於編譯器會擦除泛型參數,因此要注意,使用泛型類時儘可能類型不一致。

重載

因爲存在擦除,重載方法會產生相同的類型簽名,故:保險起見,方法名儘可能不一致。

注: 剛通了個宵,白天睡了個回籠覺,平復心情中,如今整理思緒,關於泛型的理解會慢慢在本文中補充,歡迎拍磚。

剛纔看了下,有幾位朋友提出:泛型,不是範型。錯別字,很是感謝。因爲輸入法第一個是範型,而後就沒深究,也是本身不夠嚴謹,再次感謝幾位朋友的指正。

天星技術團QQ:557247785

歡迎來擾
相關文章
相關標籤/搜索