国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java的泛型

這篇具有很好參考價值的文章主要介紹了Java的泛型。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

泛型是我們需要的程序設(shè)計手段。使用泛型機制編寫的程序代碼要比那些雜亂地使用 Object 變量,然后再進行強制類型轉(zhuǎn)換的代碼具有更好的安全性和可讀性。

至少在表面上看來,泛型很像 C++ 中的模板。與 Java —樣,在 C++ 中,模板也是最先被添加到語言中支持強類型集合的。但是,多年之后人們發(fā)現(xiàn)模板還有其他的用武之地。學習完本章的內(nèi)容可以發(fā)現(xiàn) Java 中的泛型在程序中也有新的用途。

為什么要使用泛型程序設(shè)計

泛型程序設(shè)計(Generic programming)意味著編寫的代碼可以被很多不同類型的對象所重用。例如,我們并不希望為聚集 String 和 File 對象分別設(shè)計不同的類。實際上,也不需要這樣做,因為一個 ArrayList 類可以聚集任何類型的對象。這是一個泛型程序設(shè)計的實例。

實際上,在 Java 增加泛型類之前已經(jīng)有一個 ArrayList 類。下面來研究泛型程序設(shè)計的機制是如何演變的,另外還會講解這對于用戶和實現(xiàn)者來說意味著什么。

類型參數(shù)的好處

在 Java 中增加范型類之前,泛型程序設(shè)計是用繼承實現(xiàn)的。實現(xiàn)時使用通用類型(如 Object 或 Comparable 接口),在使用時進行強制類型轉(zhuǎn)換。

泛型對于集合類尤其有用,ArrayList 就是一個無處不在的集合類。ArrayList 類維護一個 Object 類型的數(shù)組(Object 類是所有類的父類):

// before generic classes
public class ArrayList {
    private Object[] elementData;
    // ...
    public Object get(int i) { ... }
    public void add(Object o) { ... }
}

這種方法有兩個問題。

  • 當獲取一個值時,必須進行強制類型轉(zhuǎn)換。
  • 此外,這里沒有錯誤檢査??梢韵驍?shù)組列表中添加任何類的對象。對于 files.add(new File("..."); 這個調(diào)用,編譯和運行都不會出錯。然而在其他地方,如果將 get() 的結(jié)果強制類型轉(zhuǎn)換為 String 類型, 就會產(chǎn)生一個錯誤。
ArrayList files = new ArrayList();
String filename = (String) files.get(0);

泛型提供了一個更好的解決方案:類型參數(shù)(type parameters)。ArrayList 類有一個類型參數(shù)用來指示元素的類型:ArrayList<String> files = new ArrayList<String>();這使得代碼具有更好的可讀性。人們一看就知道這個數(shù)組列表中包含的是 String 對象。

在 Java7 及以后的版本中,構(gòu)造函數(shù)中可以省略泛型類型:ArrayList<String> files = new ArrayList<>();省略的類型可以從變量的類型推斷得出。

編譯器也可以很好地利用這個信息。當調(diào)用 get() 方法的時候,不需要進行強制類型轉(zhuǎn)換,編譯器就知道返回值類型為 String,而不是 Object:String filename = files.get(0);

編譯器還知道 ArrayList 中 add() 方法有一個類型為 String 的參數(shù)?,F(xiàn)在, 編譯器可以進行檢査,避免插入錯誤類型的對象。例如下面的代碼是無法通過編譯的。這將比使用 Object 類型的參數(shù)安全一些,出現(xiàn)編譯錯誤比類在運行時出現(xiàn)類的強制類型轉(zhuǎn)換異常要好得多。

files,add(new File("...")); // can only add String objects to an ArrayList<String>

類型參數(shù)的魅力在于:使得程序具有更好的可讀性和安全性。

誰想成為泛型程序員

使用像 ArrayList 這樣的泛型類很容易。大多數(shù) Java 程序員都使用 ArrayList 這樣的類型,就好像它們已經(jīng)構(gòu)建在語言之中,像 String[] 數(shù)組一樣。(當然, 數(shù)組列表比數(shù)組要好一些,因為數(shù)組列表可以自動擴容。)

但是,實現(xiàn)一個泛型類并沒有那么容易。對于類型參數(shù),使用這段代碼的程序員可能想要內(nèi)置(plugin)所有的類。他們希望在沒有過多的限制以及混亂的錯誤消息的狀態(tài)下,做所有的事情。因此,一個泛型程序員的任務就是預測出所用類的未來可能有的所有用途。

這一任務難到什么程度呢?下面是標準類庫的設(shè)計者們肯定產(chǎn)生爭議的一個典型問題。ArrayList 類的 addAll() 方法用來添加另一個集合的全部元素。程序員可能想要將 ArrayList 中的所有元素添加到 ArrayList 中去(Manager 類是 Employee 類的子類)。然而,反過來就不行了。如何設(shè)計才能只允許前一個調(diào)用,而不允許后一個調(diào)用呢?Java 語言的設(shè)計者發(fā)明了一個具有獨創(chuàng)性的新概念,通配符類型(wildcard type),它解決了這個問題。通配符類型非常抽象,然而,它們能讓庫的構(gòu)建者編寫出盡可能靈活的方法。


泛型程序設(shè)計劃分為 3 個能力級別。基本級別是,僅僅使用泛型類:典型的是像 ArrayList 這樣的集合,不必考慮它們的工作方式與原因。大多數(shù)應用程序員將會停留在這一級別上,直到出現(xiàn)了什么問題。當把不同的泛型類混合在一起時,或是在與對類型參數(shù)一無所知的遺留的代碼進行銜接時,可能會看到含混不清的錯誤消息。如果這樣的話,就需要系統(tǒng)地學習 Java 泛型來解決這些問題,而不要胡亂地猜測。當然,最終可能想要實現(xiàn)自己的泛型類與泛型方法。

應用程序員很可能不喜歡編寫太多的泛型代碼。JDK 開發(fā)人員已經(jīng)做出了很大的努力,為所有的集合類提供了類型參數(shù)。憑經(jīng)驗來說,那些原本涉及許多來自通用類型(如 Object 或 Comparable 接口)的強制類型轉(zhuǎn)換的代碼一定會因使用類型參數(shù)而受益。

本章介紹實現(xiàn)自己的泛型代碼需要了解的各種知識。希望大多數(shù)讀者可以利用這些知識解決一些疑難問題,并滿足對于參數(shù)化集合類的內(nèi)部工作方式的好奇心。

泛型類

泛型類(generic class)就是具有一個或多個類型參數(shù)的類。

本章使用一個簡單的 Pair 類作為例子。

public class Pair<T> {
    private T first;
    private T second;
    
    public Pair() { first = null ; second = null ; }
    public Pair(T first, T second) { this.first = first; this.second = second; }
    
    public T getFirst() { return first; }
    public T getSecond() { return second; }
    
    public void setFirst(T newValue) { first = newValue; }
    public void setSecond(T newValue) { second = newValue; }
}

Pair 類引入了一個類型參數(shù) T,用尖括號(< >)括起來,并放在類名的后面。

泛型類可以有多個類型參數(shù)。如果有多個類型變量,多個類型變量之間用 “,”逗號分隔。例如,可以定義 Pair 類,其中第一個域和第二個域使用不同的類型:public class Pair<T, U> { ... }

類定義中的類型參數(shù)指定方法的返回類型以及域和局部變量的類型。例如,private T first;


用具體的類型替換類型參數(shù)就可以實例化泛型類型,例如:Pair 可以將結(jié)果想象成帶有構(gòu)造器的普通類:

  • Pair()
  • Pair(String, String)

和方法:

  • String getFirst()、String getSecond()
  • void setFirst(String)、void setSecond(String)

換句話說,泛型類可看作普通類的工廠。

泛型方法

前面已經(jīng)介紹了如何定義一個泛型類。實際上,還可以定義一個帶有類型參數(shù)的簡單方法。

class ArrayAlg {
    public static <T> T getMiddle(T... a) {
        return a[a.length / 2];
    }
}

這個方法是在普通類中定義的,而不是在泛型類中定義的。然而,這是一個泛型方法,可以從尖括號和類型參數(shù)看出這一點。注意,類型參數(shù)放在修飾符(這里是 public static)的后面,返回類型的前面。


泛型方法可以定義在普通類中,也可以定義在泛型類中。

當調(diào)用一個泛型方法時,在方法名前的尖括號中放入具體的類型:

String middle = ArrayAlg.<String>getMiddle("]ohn", "Q.", "Public");

在這種情況(實際也是大多數(shù)情況)下,方法調(diào)用中可以省略 類型參數(shù)。編譯器有足夠的信息能夠推斷出所調(diào)用的方法。它用 names 的類型(即 String[])與泛型類型 T[] 進行匹配并推斷出 T 一定是 String。也就是說,可以調(diào)用

String middle = ArrayAlg.getMiddle("]ohn", "Q.", "Public");

類型參數(shù)的限定

有時,類或方法需要對類型參數(shù)加以約束。下面是一個典型的例子。我們要計算數(shù)組中的最小元素:

class ArrayAIg {
    // almost correct
    public static <T> T min(T[] a) {
        if (a == null || a.length == 0) {
            return null;
        }
        T smallest = a[0];
        for (int i = 1; i < a.length; i++) {
            if (smallest.compareTo(a[i]) > 0) smallest = a[i];
        }
        return smallest;
    }
}

但是,這里有一個問題。請看一下 min() 方法的代碼內(nèi)部。變量 smallest 類型為 T,這意味著它可以是任何一個類的對象。怎么才能確信 T 所屬的類有 compareTo() 方法呢?

解決這個問題的方案是將 T 限制為實現(xiàn)了 Comparable 接口(只含一個 compareTo() 方法的標準接口)的類??梢酝ㄟ^對類型參數(shù) T 設(shè)置限定(bound)實現(xiàn)這一點:

public static <T extends Comparable> T min(T[] a) {}

現(xiàn)在,泛型的 min() 方法只能被實現(xiàn)了 Comparable 接口的類(如 String、LocalDate 等)的數(shù)組調(diào)用。

T extends 綁定類型表示 T 應該是綁定類型的子類型(subtype)。T 和綁定類型可以是類,也可以是接口。


一個類型參數(shù)或通配符可以有多個限定,多個限定之間用 “ &” 分隔,例如:T extends Comparable & Serializable。在Java的限定中,可以根據(jù)需要擁有多個接口限定,但至多有一個類限定。如果用一個類作為限定,它必須放在限定列表中的第一個位置。

// ok
T extends Object & Comparable & Serializable
// error
T extends Comparable & Serializable & Object

泛型代碼和虛擬機

類型擦除

類型擦除是Java泛型實現(xiàn)的一種方式。

類型擦除指的是:在編譯時,將泛型類型擦除成其原始類型。

虛擬機沒有泛型類型對象,所有對象都屬于普通類。無論何時定義一個泛型類型,都自動提供了一個相應的原始類型(raw type)。類型參數(shù)用第一個限定的類型來替換,如果沒有給定限定就用 Object 替換。例如:

  • 類 Pair 中的類型參數(shù)沒有顯式的限定,因此,原始類型用 Object 替換 T。
  • 類 Interval<T extends Comparable & Serializable> 中第一個限定的類型為 Comparable,因此,原始類型用 Comparable 替換 T。

Pair 的原始類型如下所示。結(jié)果是一個普通的類,就好像泛型引入 Java 語言之前已經(jīng)實現(xiàn)的那樣。在程序中可以包含不同類型的 Pair,例如,Pair 或 Pair。而擦除類型后就變成原始的 Pair 類型了。

public class Pair {
    private Object first;
    private Object second;
    
    public Pair(Object first, Object second) {
        this.first = first;
        this.second = second;
    }
    
    public Object getFirst() { return first; }
    public Object getSecond() { return second; }
    
    public void setFirst(Object newValue) { first = newValue; }
    public void setSecond(Object newValue) { second = newValue; }
}

翻譯泛型表達式

當程序調(diào)用泛型方法時,如果擦除返回類型,編譯器插入強制類型轉(zhuǎn)換。例如,下面這個語句序列:

Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();

擦除 getFirst() 方法的返回類型后,getFirst() 方法將返回 Object 類型。編譯器自動插入 Employee 的強制類型轉(zhuǎn)換。也就是說,編譯器把這個方法調(diào)用翻譯為兩條虛擬機指令:

  • 對 Pair#getFirst() 原始方法的調(diào)用。
  • 將返回的 Object 類型強制轉(zhuǎn)換為 Employee 類型。

當存取一個泛型域時也要插入強制類型轉(zhuǎn)換。假設(shè) Pair 類的 first 域和 second 域都是公有的,表達式:Employee buddy = buddies.first;也會在結(jié)果字節(jié)碼中插入強制類型轉(zhuǎn)換。

翻譯泛型方法

類型擦除也會出現(xiàn)在泛型方法中。程序員通常認為下述的泛型方法 public static <T extends Comparable> T min(T[] a)是一個完整的方法族,而擦除類型之后,只剩下一個方法:public static Comparable min(Comparable[] a)

注意,類型參數(shù) T 已經(jīng)被擦除了,只留下了限定類型 Comparable。


類型擦除帶來了兩個復雜問題??匆豢聪旅孢@個示例:

class DateInterval extends Pair<LocalDate> {
    public void setSecond(LocalDate second) {
        if (second.compareTo(getFirst()) >= 0) {
            super.setSecond(second);
        }
    }
    ...
}

一個日期區(qū)間是一對 LocalDate 對象,并且需要覆蓋這個方法來確保第二個值永遠不小于第一個值。這個類擦除后變成

// after erasure
class DateInterval extends Pair {
    public void setSecond(LocalDate second) { ... }
    ...
}

令人感到奇怪的是,存在另一個從 Pair 繼承的 setSecond() 方法,即 public void setSecond(Object second)這顯然是一個不同的方法,因為它有一個不同類型的參數(shù) Object,而不是 LocalDate。然而,不應該不一樣。考慮下面的語句序列:

DateInterval interval = new DateInterval(...);
Pair<LocalDate> pair = interval; // OK assignment to superclass
pair.setSecond(aDate);

這里,希望對 setSecond() 的調(diào)用具有多態(tài)性,并調(diào)用最合適的那個方法。由于 pair 引用 Datelnterval 對象,所以應該調(diào)用 Datelnterval.setSecond()。問題在于類型擦除與多態(tài)發(fā)生了沖突。要解決這個問題,就需要編譯器在 Datelnterval 類中生成一個橋方法(bridge method):

public void setSecond(Object second) { setSecond((Date) second); }

有關(guān)泛型的事實

需要記住有關(guān) Java 泛型轉(zhuǎn)換的事實:

  • 虛擬機中沒有泛型,只有普通的類和方法。
  • 所有的類型參數(shù)都用它們的限定類型替換。
  • 橋方法被合成來保持多態(tài)。
  • 為保持類型安全性,必要時插入強制類型轉(zhuǎn)換。

類 A 是類 B 的子類,但是 G 和 G 不具有繼承關(guān)系,二者是并列關(guān)系。

public static void printBuddies(Pair<Employee> p) { ... }

// Manager 類是 Employee 類的子類
Pair<Manager> pair = new Pair<>();
// error(固定的泛型類型系統(tǒng)的局限,通配符類型解決了這個問題)
printBuddies(pair);

泛型一般有三種使用方式:泛型類、泛型方法、泛型接口。

// 泛型類
public class Pair<T>
// 實例化泛型類
Pair<String> pair = new Pair<>();
// 繼承泛型類,指定類型
class DateInterval extends Pair<LocalDate>

// 泛型方法
public static <T> T getMiddle(T... a)

// 泛型接口
public interface Generator<T>
// 實現(xiàn)泛型接口,指定類型
class GeneratorImpl implements Generator<String>

通配符類型

固定的泛型類型系統(tǒng)使用起來并沒有那么令人愉快,類型系統(tǒng)的研究人員知道這一點已經(jīng)有一段時間了。Java 的設(shè)計者發(fā)明了一種巧妙的(仍然是安全的)“解決方案”:通配符類型。下面幾小節(jié)會介紹如何處理通配符。

通配符概念

通配符類型中,允許類型參數(shù)變化。例如,通配符類型 Pair<? extends Employee> 表示任何泛型 Pair 類型,它的類型參數(shù)是 Employee 的子類,如 Pair,但不是 Pair

假設(shè)要編寫一個打印雇員對的方法,像這樣:

public static void printBuddies(Pair<Employee> p) {
    Employee first = p.getFirst();
    Employee second = p.getSecond();
    System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
}

正如前面講到的,不能將 Pair 傳遞給這個方法,這一點很受限制。解決的方法很簡單:使用通配符類型:

public static void printBuddies(Pair<? extends Employee> p)

類型 Pair 是 Pair<? extends Employee> 的子類型(如圖 8-3 所示)。

Java的泛型


使用通配符會通過 Pair<? extends Employee> 的引用破壞 Pair 嗎?

Pair<Manager> managerBuddies = new Pair<>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // compile-time error

這可能不會引起破壞。對 setFirst() 的調(diào)用有一個類型錯誤。要了解其中的緣由,請仔細看一看類型 Pair<? extends Employee>。其方法似乎是這樣的:

? extends Employee getFirst()
void setFirst(? extends Employee)

這樣將不可能調(diào)用 setFirst() 方法。編譯器只知道需要某個 Employee 的子類型,但不知道具體是什么類型。它拒絕傳遞任何特定的類型。畢竟 ? 不能用來匹配。

使用 getFirst() 就不存在這個問題:將 getFirst() 的返回值賦給一個 Employee 的引用完全合法。這就是引入有限定的通配符的關(guān)鍵之處?,F(xiàn)在已經(jīng)有辦法區(qū)分安全的訪問器方法和不安全的更改器方法了。

通配符的超類型限定

通配符限定與類型參數(shù)限定十分類似,但是,通配符限定還有一個附加的能力,即可以指定一個超類型限定(supertype bound),如下所示:? super Manager。

這個通配符限制為 Manager 的所有超類型。(已有的 super 關(guān)鍵字十分準確地描述了這種聯(lián)系)

參考資料

《Java核心技術(shù)卷一:基礎(chǔ)知識》(第10版)第 8 章:泛型程序設(shè)計文章來源地址http://www.zghlxwxcb.cn/news/detail-480755.html

到了這里,關(guān)于Java的泛型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • Java 中的泛型(兩萬字超全詳解)

    Java 中的泛型(兩萬字超全詳解)

    博主將用 CSDN 記錄 Java 后端開發(fā)學習之路上的經(jīng)驗,并將自己整理的編程經(jīng)驗和知識分享出來,希望能幫助到有需要的小伙伴。 博主也希望和一直在堅持努力學習的小伙伴們共勉!唯有努力鉆研,多思考勤動手,方能在編程道路上行至所向。 由于博主技術(shù)知識有限,博文中

    2024年02月04日
    瀏覽(16)
  • Java 中的泛型是什么,它有什么作用?(十五)

    Java中的泛型是一種類型參數(shù)化機制,它使代碼更具可讀性、可重用性和穩(wěn)健性。在Java中,通過使用泛型,可以將類型作為參數(shù)傳遞給類或方法,并在編譯時執(zhí)行類型檢查,從而避免許多運行時錯誤。 泛型的基礎(chǔ) Java泛型的基礎(chǔ)概念是類型變量、類型參數(shù)和類型邊界。 類型變

    2024年02月03日
    瀏覽(26)
  • 第8章-第1節(jié)-Java中的泛型(參數(shù)化類型)

    1、泛型在java中有很重要的地位,在面向?qū)ο缶幊碳案鞣N設(shè)計模式中有非常廣泛的應用。什么是泛型?為什么要使用泛型? 2、概念:在類聲明體中用到了類型參數(shù)。 3、泛型類只支持類類型,不支持基本數(shù)據(jù)類型(如int),但可以用包裝類(如Integer ) 泛型標識 含義 T Type 類

    2024年01月23日
    瀏覽(25)
  • TypeScript中的泛型(泛型函數(shù)、接口、類、泛型約束)

    一、泛型函數(shù) TypeScript 泛型是一種可以使代碼具有更高的可重用性和泛化能力的特性 。通過泛型,我們可以定義一種通用的類型或函數(shù),使其能夠應對多種類型的輸入。泛型在類、函數(shù)、接口等多種場景下都可以使用。 具體來說,在定義泛型函數(shù)時,我們可以使用來表示一

    2024年02月11日
    瀏覽(23)
  • 【TypeScript】TypeScript中的泛型

    定義一個函數(shù)或類時,有些情況下無法確定其中要使用的具體類型(返回值、參數(shù)、屬性的類型不能確定),此時泛型便能夠發(fā)揮作用。 舉個例子: 上例中,test函數(shù)有一個參數(shù)類型不確定,但是能確定的時其返回值的類型和參數(shù)的類型是相同的,由于類型不確定所以參數(shù)和

    2024年02月09日
    瀏覽(30)
  • 第8章 泛型程序設(shè)計

    第8章 泛型程序設(shè)計

    泛型程序設(shè)計(generi c programming)意味著編寫的代碼可以對多種不同類型的對象重 用。 它們會讓你的程序更易讀,也更安全。 集合中沒有使用泛型時: 集合中使用泛型時: Java泛型可以保證如果程序在編譯時沒有發(fā)出警告,運行時就不會產(chǎn)生ClassCastException異常。即,把不安全的

    2024年02月07日
    瀏覽(10)
  • C++中的泛型詳細講解

    它是一種泛化的編程方式,其實現(xiàn)原理為程序員編寫一個函數(shù)/類的代碼示例,讓編譯器去填補出不同的函數(shù)實現(xiàn)。允許您延遲編寫類或方法中的編程元素的數(shù)據(jù)類型的規(guī)范,直到實際在程序中使用它的時候。換句話說,泛型允許您編寫一個可以與任何數(shù)據(jù)類型一起工作的類或

    2024年02月12日
    瀏覽(31)
  • python中的泛型使用TypeVar

    PEP484的作者希望借助typing模塊引入類型提示,不改動語言的其它部分。通過精巧的元編程技術(shù),讓類支持[]運算不成問題。但是方括號內(nèi)的T變量必須在某處定義,否則要大范圍改動python解釋器才能讓泛型支持特殊的[]表示法。 鑒于此,我們增加了typing.TypeVar構(gòu)造函數(shù),把變量

    2024年02月04日
    瀏覽(25)
  • Kotlin中的泛型理解與應用

    泛型是一種在編程中用于增加代碼的靈活性和重用性的概念。它允許我們編寫可以在多種類型上工作的通用代碼,而不需要為每種類型都編寫不同的代碼。 在Kotlin中,泛型可以應用于類、函數(shù)和接口等。下面是一些關(guān)于泛型在Kotlin中的理解和示例。 1、泛型類 泛型類是指可以

    2024年02月07日
    瀏覽(24)
  • .NET Framework中自帶的泛型委托Func

    .NET Framework中自帶的泛型委托Func

    Func是.NET Framework中自帶的泛型委托,可以接收一個或多個輸入?yún)?shù),并且有返回值,和Action類似,.NET基類庫也提供了多達16個輸入?yún)?shù)的Func委托,輸出參數(shù)只有1個。 .NET Framework為我們提供了多達16個參數(shù)的Func委托定義,對于常見的開發(fā)場景已經(jīng)完全夠用。 如下圖, 注意:

    2024年02月05日
    瀏覽(16)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包