1. 程式人生 > >基礎篇——泛型(Generics)

基礎篇——泛型(Generics)

寫程式碼的四點:

     1.明確需求。要做什麼?

     2.分析思路。要怎麼做?(1,2,3……)

     3.確定步驟。每一個思路要用到哪些語句、方法和物件。

     4.程式碼實現。用具體的語言程式碼將思路實現出來。

學習新技術的四點:

     1.該技術是什麼?

     2.該技術有什麼特點?(使用需注意的方面)

     3.該技術怎麼使用?(寫Demo)

     4.該技術什麼時候用?(在Project中的使用場景 )

----------------------早計劃,早準備,早完成。------------------------

為什麼要使用泛型?

        Java設計之初並不知道會往容器中存放什麼型別的元素,因此元素型別都設定為Object,這樣就什麼都能放了。但是這麼設計有明顯的缺點:①取出元素的時候必須進行強制型別轉換異常;②如果不小心往集合里加了不相同型別的元素可能會導致型別異常,進行equals、compare比較的時候尤為明顯;③在很多地方都需要強制型別轉換,增加了變成的複雜度,影響了程式碼的美觀和維護成本。

        因此泛型應運而生。

泛型概述:

        泛型(generics)是Java 1.5中引入的新特性,泛型提供了編譯時型別安全檢測機制,該機制允許程式設計師在編譯時檢測到非法的型別。只要編譯時不出現問題,執行時就不會出現ClassCastException(型別轉換異常)。

        泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。

        Java集合都實現了泛型,允許程式在建立集合時就可以指定集合元素的型別,例如List<String>就表明這是一個只能存放String型別的List。List<String>就是引數化型別,也就是泛型,而String就是該List<String>泛型的型別引數。

        泛型只在編譯階段有效,在編譯之後會進行泛型擦除的操作,不會傳遞到執行階段。也就是說編譯後的class檔案中是不包含任何泛型資訊的。

泛型的規則:

        1.泛型的型別引數只能是類型別(包括自定義類)和萬用字元,不能是簡單型別;

        2.同一種泛型可以對應多個版本(型別引數不同),不同版本的泛型類例項是不相容的;

        3.泛型的型別引數可以有多個,例如<K , V>;

        4.泛型的型別引數可以使用extends語句,形如<T extends Number>稱為有界的型別引數。

        5.不能對確切的泛型型別使用 instanceof 操作;

            注:A  instanceof  B:判斷A是否是B的例項物件或者B子類的例項物件。

        6.不能建立一個確切的泛型型別的陣列;

泛型的好處:

        1.在編譯的時候檢查型別安全,減少了執行時異常,降低crash率;保證瞭如果在編譯時沒有發出警告,則在執行時就一定不會產生ClassCastException(型別轉換異常);

        2.集合建立時就指定了集合元素的型別,取出元素的時候不需要強制型別裝換了;

        3.提高了程式碼的複用性、減少維護成本;

泛型類:

        1.定義一個類,在該類名後面新增型別引數宣告部分(由尖括號分隔)。

        2.每一個型別引數宣告部分可以包括一個或多個型別引數,引數間用逗號隔開。

        3.使用<T>來宣告一個型別持有者名稱,然後就可以把T當作一個型別來宣告成員、引數、返回值型別。T僅僅是個名字,可以自行定義。

/**
 * 泛型類
 */

public class GenericsBox<T> {
    private T t;

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

    public T get() {
        return t;
    }
}
        //泛型類呼叫
        GenericsBox<Integer> integerGenericsBox = new GenericsBox<>();
        GenericsBox<String> stringGenericsBox = new GenericsBox<>();

        integerGenericsBox.add(new Integer(25));
        stringGenericsBox.add(new String("年齡"));
        LogUtil.e("泛型類呼叫", stringGenericsBox.get() + ":" + integerGenericsBox.get());

泛型介面:

        1.定義一個介面,在該介面名後面新增型別引數宣告部分(由尖括號分隔)。

        2.每一個型別引數宣告部分可以包括一個或多個型別引數,引數間用逗號隔開。

/**
 * 泛型介面
 */

public interface GenericsInterface<T> {

    public abstract void genericsInterface1(T element);

    public abstract <T> void genericsInterface2();
}

泛型方法:

        定義一個泛型方法,該方法在呼叫時可以接收不同型別的引數。根據傳遞給泛型方法的引數型別,編譯器適當的處理每一個方法呼叫。

        定義泛型方法的規則:

        1.所有泛型方法宣告都有一個型別引數宣告部分(由尖括號分隔),該型別引數宣告部分在方法返回值型別之前;

        2.每一個型別引數宣告部分包括一個或多個型別引數,引數間用逗號隔開。一個泛型引數,也被稱為一個型別變數,是用於指定一個泛型型別名稱的識別符號;

        3.型別引數能被用來宣告返回值型別,並且能作為泛型方法得到的實際引數型別的佔位符;

    /**
     * 泛型方法
     * 列印各種型別的陣列中的元素
     *
     * @param inputArray
     * @param <T>
     */
    public static <T> void printLog(T[] inputArray) {
        for (T element : inputArray) {
            LogUtil.e("列印陣列中的元素", element + "");
        }
    }
        //建立各種型別的陣列(Integer、Double、Character)
        Integer[] integerArray = {1, 2, 3, 4, 5};
        Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5};
        Character[] characterArray = {'a', 'b', 'c', 'd', 'e'};

        printLog(integerArray);
        printLog(doubleArray);
        printLog(characterArray);

靜態方法與泛型:

        如果在類中定義使用泛型的靜態方法,需要新增額外的泛型型別引數宣告,即將該靜態方法定義為泛型方法。即使靜態方法要使用泛型類中已經宣告過的型別引數也不可以。

/**
 * 靜態方法與泛型
 */

public class GenericsStatic<T> {

    public static <T> void show(T t) {
    }

    //以下是錯誤的
//    public static void show(T t) {
//    }
}

有界的型別引數:

        限定被允許傳遞到一個型別引數的型別種類範圍。

        1.要宣告一個有界的型別引數,首先列出型別引數的名稱,後跟extends關鍵字,繼承上界。例如:上界通過形如<T extends Comparable<T>>來定義,表示型別只能接受Comparable及其下層子類型別。這就是有界的型別引數的目的。

        注意:<T extends Comparable<T>>這裡的限定使用關鍵字extends,後面可以是類也可以是介面。extends統一的表示了原有的extends和implements的概念,應該理解為T型別是實現了Comparable介面的型別,或者是繼承了XX類的型別。

        2.泛型仍要遵循Java單繼承、多實現的體系,所以當某個型別引數需要使用extends限定,且有多種型別的時候,只能存在一個類,並且類寫在第一位,介面列在後面。形如<T extends Number & Comparable & Callback>

    /**
     * 泛型方法
     * 比較三個值並返回最大值
     *
     * @param x
     * @param y
     * @param z
     * @param <T>
     * @return
     */
    public static <T extends Comparable<T>> T maxValue(T x, T y, T z) {
        //假設x是最大值
        T max = x;

        //如果y比max大,則將y賦值給max
        if (y.compareTo(max) > 0) {
            max = y;
        }
        //如果z比max大,則將z賦值給max
        if (z.compareTo(max) > 0) {
            max = z;
        }
        //返回最大值
        return max;
    }
        //輸出最大值
        LogUtil.e("最大值為:", maxValue(3, 7, 5) + "");
        LogUtil.e("最大值為:", maxValue(3.3, 3.7, 5.3) + "");
        LogUtil.e("最大值為:", maxValue("apple", "pear", "orange"));

型別萬用字元(?)

        為了解決型別被限制死了不能動態根據例項來確定的缺點,引入了型別萬用字元。型別萬用字元一般使用 ? 代替具體的型別引數。例如:List<?>在邏輯上是List<String>、List<Integer>等所有List<具體型別實參>的父類。

        1.如果只指定了<?>,而沒有extends,則預設是允許Object及其下的任何Java類,也就是任意類。

        2.萬用字元也可以向上限制,通過形如List<? extends Comparable>來定義,表示型別只能接受Comparable及其下層子類型別;

        3.萬用字元還可以向下限制,通過形如List<? super Comparable>來定義,表示型別只能接受Comparable及其上層父類型別;

        因為getData()的引數是List<?>型別,所以name、age、number都可以作為這個方法的實參,這就是萬用字元的作用。

        因為getNumberData()的引數是有上限的List<? extends Number>型別的,如此定義就是型別萬用字元泛型值接受Number及其下層子類型別。

    /**
     * 型別萬用字元的使用
     */
    public static void getData(List<?> data) {
        LogUtil.e("型別萬用字元的使用", data.get(0) + "");
    }
    
    public static void getNumberData(List<? extends Number> data) {
        LogUtil.e("型別萬用字元的使用(Number)", data.get(0) + "");
    }
        //型別萬用字元
        List<String> name = new ArrayList<>();
        List<Integer> age = new ArrayList<>();
        List<Character> blood = new ArrayList<>();

        name.add("張三");
        age.add(25);
        blood.add('A');

        getData(name);
        getData(age);
        getData(blood);

//        getNumberData(name);
        getNumberData(age);
//        getNumberData(blood);

萬用字元的PECS原則:

        1.Producer  Extends:使用<?  extends  T>的集合類,只能作為Producer(生產者)向外提供(get)元素;而不能作為Consumer(消費者)對外獲取(add)元素;

        2.Consumer  Super:使用<?  super  T>的集合類,只能作為Consumer(消費者)對外獲取(add)元素;而不能作為Producer(生產者)向外提供(get)元素;

        3.如果同時需要讀取以及寫入,那就不能使用萬用字元。

        //萬用字元的PECS原則
        List<? extends String> extendsList = new ArrayList<>();
        List<? super String> superList = new ArrayList<>();

//        extendsList.add("李四");
        String s = extendsList.get(0);
        superList.add("張三");
//        Object s1 = superList.get(0);

相關推薦

基礎——Generics

寫程式碼的四點:      1.明確需求。要做什麼?      2.分析思路。要怎麼做?(1,2,3……)      3.確定步驟。每一個思路要用到哪些語句、方法和物件。      4.程式碼實現。用具體的語言程式碼將思路實現出來。 學習新技術的四點:      

javaSEGenerics

運行時 str nts super 也有 get 基本類型 簡介 pre 前言 這幾天分享了怎麽搭建集群,這一篇給大家介紹的是泛型,在我們的很多java底層的源代碼都是有很多復雜的泛型的!那什麽是泛型呢? 泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,

CLR via C#關於Generics 的摘錄

類庫 prope png param [] ron using 排序算法 相互 泛型,是CLR和編程語言提供的一種特殊機制,它支持另一種形式的代碼重用,即“算法重用”。 簡單的說,開發人員先定義好一個算法,比如排序、搜索、交換、比較或者轉換等。但是

java8教程-Generics

泛型(已更新) 在任何繁瑣的(nontrivial)軟體專案中,bug是家常便飯。細心的規劃,程式設計和測試可以幫助減少bug的普遍性(pervasiveness),但是無論如何,無論在哪裡,bug總會伺機悄悄溜進(creep)你的程式碼,因為很明顯,

對照Java學習Swift--Generics

簡介 泛型程式碼讓你能夠根據自定義的需求,編寫出適用於任意型別、靈活可重用的函式及型別。它能讓你避免程式碼的重複,用一種清晰和抽象的方式來表達程式碼的意圖。從Java1.5開始,引進了泛型,Swift和Java的泛型很類似,都很強大,學過Java的同學都知道。

Generics和集合

 -----------------------------------------------------------------------------------------------------------------------------------------

【Java】Generics

What    顧名思義,泛型:一般型別,也就是說可以為任何型別,泛型的本質是“引數化型別”,也就是說所操作的資料型別被指定為一個引數。泛型是在JDK1.5中引入的特性。 Why    泛型提供了編譯時型別安全檢測機制,該機制允

Java基礎深入解讀1

一名合格的Java程式設計師,當然要經常翻翻JDK的原始碼。經常看JDK的API或者原始碼,我們才能更加了解JDK,才能更加熟悉底層。 一、引出泛型 然而,在看原始碼的過程中,我們經常會看到類似於如下這樣的程式碼: private void putAllForCreat

java基礎==========Android

這一篇將系統的回憶起泛型的知識 在做Android的時候用到泛型的類有很多,很多時候會遇到將資料儲存到一個List集合,Map等等,他們之間使用<>來填寫資料型別,說一個我現在想到的就是Android中非同步載入這個類AsyncTask《Pa

C# 基礎知識系列- 10 反射和

0. 前言 這篇文章延續《C# 基礎知識系列- 5 反射和泛型》,繼續介紹C#在反射所開發的功能和做的努力。上一篇文章大概介紹了一下泛型和反射的一些基本內容,主要是通過獲取物件的型別,然後通過這個型別物件操作物件。這一篇介紹一個在反射中很重要的內容:特性,以及上一篇未完成的內容——泛型在反射中的引用。 1.

JAVA

強制 off 實例 emp 思想 void 成了 意義 依然 一. 泛型概念的提出(為什麽需要泛型)? 首先,我們看下下面這段簡短的代碼: 1 public class GenericTest { 2 3 public static void

CLR類設計之

where條件 之前 解釋 columns 文章閱讀 sin 自己的 讀書 spl 在上一篇文章中,介紹了什麽是泛型,以及泛型和非泛型的區別,這篇文章主要講一些泛型的高級用法,泛型方法,泛型泛型接口和泛型委托,協變和逆變泛型類型參數和約束性,泛型的高

基礎之集合List總結

intern ansi [] 集合 add 引用 public log ++ 1. List集合下常用的集合(ArrayList,LinkedList,Vector);   JVM垃圾回收GC,Java中采取了可達性分析法,標記所有從根節點開始的可達對象,未被標記的對象就

基礎之集合總結

線程不安全 emp abstract 和集 write next 不可變 叠代器 關系 1. Map集合和collection結合的區別 1》Collection一次存一個元素;Map一次存一對元素; 2》Collection是單列集合;Map是雙列集合; 3》Map中的存

Java:入門、原理、使用

core clas set out keyword getclass code 避免 post 遠在 JDK 1.4 版本的時候,那時候是沒有泛型的概念的。當時 Java 程序員們寫集合類的代碼都是類似於下面這樣: List list = new ArrayList();

Scala 語言學習之7

scala 泛型==> 泛型類 ---> T 可以代表任意類型class Person[T]{ private var name:T = _ def setName(name:T) = {this.name = name} def getName():T = {this

out print ret 包裝類 基本類型 使用 我們 urn red   面向對象的重要目標就是代碼的重用,支持這一目標的一個重要機制就是泛型。如果除去對象的基本類型外實現的方法是相同的,那麽我們就可以用泛型機制來描述這種基本的功能。 在1.5版本以前,Java並不直接

spa ring private 結果 最大值 swa () integer length   上一次我分享了使用繼承來實現泛型,今天講一下使用接口類型表示泛型。 只有在使用Object已有的那些方法能夠表示所執行的操作時,才能使用Object表示泛型,例如要比較一些圖形的

C# - box - unbox - Generic

com img 9.png bsp inf image 技術分享 ID .com 泛型: C# - box - unbox - 泛型(Generic)

詳解C#

安全 情況 重用 模板 信息 普通 cast 綁定 封閉式   一、C#中的泛型引入了類型參數的概念,類似於C++中的模板,類型參數可以使類型或方法中的一個或多個類型的指定推遲到實例化或調用時,使用泛型可以更大程度的重用代碼、保護類型安全性並提高性能;可以創建自定義的泛型類