1. 程式人生 > 實用技巧 >7天用Go從零實現Web框架Gee教程

7天用Go從零實現Web框架Gee教程

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

1、簡單泛型

  • 泛型的出現最引人注目的一個原因,就是為了創造容器類。容器就是存放要使用的物件的地方。

  • 通常而言,我們只會使用容器來儲存一種型別的物件。泛型的主要目的之一就是用來指定容器要持有什麼型別的物件,而且由編譯器來保證型別的正確性。

  • 暫不指定型別,稍後再決定具體使用什麼型別。要達到這個目的,需要使用型別引數,用尖括號括住,放在類名後面。然後在使用這個類的時候,再用實際的型別替換型別引數。

  • 在使用泛型時,我們只需指定它們的名稱以及型別引數列表即可。

  • 元組是將一組物件直接打包儲存於其中的一個單一物件。這個容器物件允許讀取其中元素,但不允許向其中存放新的物件。元組可以具有任意長度,同時元組中的物件可以是任意不同的型別。例如二維元組:class TwoTuple<A,B>{}

2、泛型介面

  • 泛型可應用於介面。

  • 沒擁有原始碼的控制權,又不想重寫一個類,我們可以建立一個介面卡來實現所需的介面。通過繼承存在的源類建立介面卡類。

3、泛型方法

  • 是否擁有泛型方法,與其所在的類是否泛型沒有關係。泛型方法使得該方法能夠獨立於類而產生變化。如果使用泛型方法可以取代將整個類泛型話,那麼就應該只使用泛型方法。對於一個static方法而言,無法訪問泛型類的型別引數,因此,static方法需要使用泛型能力,就必須使其成為泛型方法。

  • 定義泛型方法,只需將泛型引數列表置於返回值之前。

  • 當使用泛型類時,必須在建立物件的時候指定型別引數的值,而使用泛型方法的時候,通常不必指明引數型別,因為編譯器會為我們找出具體的型別。這稱為型別引數推斷(type argument inference)。

  • 型別引數推斷避免了重複的泛型引數列表。例如:

    Map<Person, List<? extends Pet>> petPeople =new HashMap<Person, List<? extends Pet>>();

    可轉換為:

    public static <K,V> Map<K,V> map() {

return new HashMap<K,V>();

}

Map<Person, List<? extends Pet>> petPeople = map();

雖然下面那種寫法為我們帶來的好處不多,但有時如果能使用還是使用。

  • 型別推斷只對賦值操作有效,其他時候並不起作用。如果將一個泛型方法呼叫返回的結果(例如map())作為引數,傳遞給另一個方法,編譯器並不會執行型別推斷。在這種情況下,編譯器會認為:呼叫泛型方法後,其返回值被賦給一個Object型別的變數。

  • 泛型方法與可變引數列表能夠很好地共存。

  • 一個Set實用工具

    171452_4dbQ_570654.png

    這四個方法表達瞭如下的數學集合操作:union()返回一個Set,它將兩個引數合併在一起;intersection()返回的Set只包含兩個引數共有的部分;difference()方法從superset中移除subset包含的元素;complement()返回的Set包含除了交集之外的所有元素(補集)。

4、匿名內部類

  • 泛型可用於內部類以及匿名內部類

5、構建複雜模型

  • 泛型的一個重要好處就是能夠簡單而安全地建立複雜的模型。例如,我們可以很容易地建立List元組:

    172443_7cZQ_570654.png

    儘管看上去有些冗長(特別是迭代器的建立),但最終還是沒用過多程式碼就得到一個相當強大的資料結構。

6、擦除的神祕之處

  • 可以宣告ArrageyList.class,但是不能宣告ArrayList<Integer>.class。

  • 不同的型別在行為方面肯定不同。ArrayList<String>和ArrayList<Integer>行為不同,但是它們是相同的型別。

  • 在泛型程式碼內部,無法獲取任何有關泛型引數型別的資訊。

  • Java泛型是使用擦除來實現的,這意味著當你使用泛型時,任何具體的型別資訊都被擦除,你唯一知道的事你在使用一個物件。因此ArrayList<String>和ArrayList<Integer>在執行時事實上是相同的型別,這兩種形式都被擦除成它們的“原生”型別,即ArrayList。

  • 協助泛型類,給定泛型的邊界,以此來告知編譯器只能接受遵循這個邊界的型別,這裡重用extends關鍵字。泛型型別引數將擦除到它的第一個邊界(它可能有多個邊界)。當你希望程式碼能夠跨多個類工作時,使用泛型才有所幫助。必須檢視所有程式碼,並確定它是否“足夠複雜”到必須使用泛型的程度。

  • 擦除的代價是顯著的。泛型不能用於顯示地引用執行時型別的操作之中,例如轉型、instanceof操作和new表示式,因為所有引數的型別資訊都丟失了。

  • 即使擦除在方法或類內部移除的了有關實際型別的資訊,編譯器仍舊可以確保在方法或類中使用的型別的內部一致性。因為擦除在方法體中移除了型別資訊,所以在執行時的問題是邊界:即物件進入和離開方法的地點。這些正是編譯器在編譯器執行型別檢查並插入轉型程式碼的地點。

7、擦除的補償

  • 擦除導致任何在執行時需要知道確切型別資訊的操作都將無法工作。偶爾可以繞過這些問題來程式設計,但有時必須通過引入型別標籤來對擦除進行補償。這就意味著你需要顯示地傳遞你的型別的Class物件,以便你可以在型別表示式中使用它。

  • 引入型別標籤,就可以使用isInstance()方法。

  • 使用型別標籤,就可以使用newInstance()來建立這型別的新物件。

8、邊界

  • 邊界是在用於泛型的引數型別上設定限制條件,強制規定泛型可應用的型別,其中有個潛在的效果是可按照自己的邊界型別來呼叫方法。擦除移除了型別資訊,因此可用無界泛型引數呼叫的方法只是那些可用Object呼叫的方法。

  • 為了執行這種限制,java重用了extends關鍵字。

  • 注意,萬用字元被限制為單一邊界

9、萬用字元

  • Apple繼承Fruit,成為一種Fruit型別,但Apple的List在型別上仍不等價於Fruit的List。真正的問題是我們在談論容器的型別,而不是容器持有的型別。

  • 有時你想要在兩個類之間建立某種型別的向上轉型關係,這正是萬用字元所允許的:

    List<? extends Fruit> flist = new ArrayList<Apple>();

這實際上並不意味著這個List持有任何型別的Fruit。一旦執行這種型別的向上轉型,你就將丟失掉向其中傳遞任何對 象的能力,甚至是傳遞Object也不行。另一方面,呼叫一個返回Fruit的方法,則是安全的。

如果先將Apple向上轉型為Fruit——編譯器將直接拒絕對引數列表中涉及萬用字元的方法呼叫。

引數型別是Object,不涉及任何萬用字元,編譯器將允許這個呼叫。這意味著將由泛型類的設計者來決定哪些呼叫 是“安全的”,並使用Object型別作為引數。

  • 超型別萬用字元:可宣告萬用字元是由某個特定類的任何基類來界定的,方法是指定<? super MyClass>,甚至或者使用型別引數:<? super T>。有了超型別萬用字元,就可以向集合中寫入了。

  • 無界萬用字元<?>:無界萬用字元看起來好像等價於使用原生型別,其實不然。例如,List實際上表示“持有任何Object型別的原生List”,而List<?>表示“具有某種特定型別的非原生List,只是我們不知道那種型別是什麼”。

  • 確切的泛型型別來替代萬用字元的好處是,可以用泛型引數做更多的事,但是使用萬用字元使得你必須接受範圍更寬的引數化型別作為引數。必須逐個情況權衡利弊,找到合適你需求的方法。

10、動態型別安全

  • java.util.Collections中提供了一組解決型別檢查問題的工具,它們是:靜態方法checkedCollection()、checkedList()、checkedMap()、checkedSet()、checkedSortedMap()、checkedSortedSet()。這些方法每個都將動態檢查的容器當作第一個引數接收,並將強制要求的型別作為第二個引數接收。

轉載於:https://my.oschina.net/90liusq/blog/341140