<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] ## 協變與逆變 類或者接口上的泛型參數可以添加out或者in關鍵字。 對于泛型類型參數, * **out關鍵字**用于指定該類型參數是**協變Covariant**; * **in關鍵字**用于指定該類型參數是**逆變Contravariance**。 協變與逆變其實是C#語言4.0以后新增的高級特性, * **協變是將父類變為具體子類,協變類型作為消費者,只能讀取不能寫入**, * **逆變是將子類變為具體父類,逆變作為生產者,只能寫入不能讀取**。 Kotlin語言在1.0版本時就將其引入到語法體系中來了。本節我們將對協變與逆變進行詳細講解。 ### 協變 在上一小節中的腳下留心中講到,B類是A類的子類型,默認情況下Xxx<B>不是Xxx<A>的子類型。但是**通過out關鍵字可以使Xxx<B>是Xxx<A>的子類型,這樣的操作叫作協變**。 接下來我們稍微修改上面的代碼來通過out關鍵字可以讓Xxx<B>是Xxx<A>的子類型。修改后的代碼如下所示。 ``` open class Animal { fun bathe() { println("開開心心地洗澡...") } } class Cat : Animal() //貓類 class PetShop<T : Animal>(var animals: List<T>)//寵物店類 //幫所有的寵物洗澡 fun batheAll(petShop: PetShop<out Animal>) { for (animal: Animal in petShop.animals) { animal.bathe() //開始洗澡 } } fun main(args: Array<String>) { val cat1 = Cat() //第1只貓 val cat2 = Cat() //第2只貓 val animals = listOf<Cat>(cat1, cat2) //寵物裝入一個集合 val petShop = PetShop<Cat>(animals) //將寵物送到寵物店 batheAll(petShop) // 編譯器報錯 } ``` 運行結果: ``` 開開心心地洗澡… 開開心心地洗澡… ``` 上述代碼中,雖然Cat是Animal的子類型,但是`PetShop<Cat>`不是`PetShop<Animal>`的子類型,在第19行代碼中調用batheAll()方法后程序正常運行,并不會像之前示例中的代碼出現類型不匹配的編譯問題,這是因為**在batheAll()方法的參數中添加了一個out關鍵字,這個關鍵字可以幫助泛型參數`<Animal>`支持協變(將父類變為具體子類)**。 >[info] **總結** out關鍵字使用的幾種情況 (1)out關鍵字**只能出現在泛型類或者泛型接口的泛型參數聲明上**,不能出現在泛型方法的泛型參數聲明上。 (2)out關鍵字修飾泛型類或者泛型接口的泛型參數時會**支持協變**的 ### 逆變 根據上面章節中的腳下留心的內容可知,如果A是B的子類型,默認情況下Xxx<A>不是Xxx<B>的子類型,通過out關鍵字使Xxx<A>變成Xxx<B>的子類型時,這樣的變化叫作協變。與out關鍵字對應的是in關鍵字,in關鍵字與out關鍵字有相反的功能,可以使Xxx<A>不是Xxx<B>的子類型,這樣的變化叫作逆變。 接下來我們通過一個案例來驗證in關鍵字使泛型參數支持逆變,具體代碼如下所示。 ``` open class Animal class Cat : Animal() {} class Dog : Animal() {} //定義寵物店類 class PetShop<in T> { fun feed(animal: T) { if (animal is Cat) { println("喂食小貓...") } else if (animal is Dog) { println("喂食小狗...") } } } //定義喂貓的方法 fun feedCat(petShop: PetShop<Cat>): Unit { petShop.feed(Cat()) } fun main(args: Array<String>) { feedCat(PetShop<Animal>()) } ``` 運行結果: ``` 喂食小貓… ``` 上述代碼中,feedCat()方法中需要傳遞的參數類型是`PetShop<Cat>`,在第19行調用這個方法時,傳遞的是`PetShop<Animal>`類型的參數,程序沒報錯并且運行成功。根據代碼可知Cat是Animal的子類型,根據運行結果可知PetShop<Animal>是PetShop<Cat>子類型,這是由于在第5行代碼中PetShop<in T>泛型參數上使用了in關鍵字,**in關鍵字使泛型參數產生了逆變(將子類變為具體父類)**。 >[info]總結 in關鍵字使用的幾種情況 (1)in關鍵字**可以出現在泛型類或者泛型接口的泛型參數聲明**上,**不能出現在泛型方法的泛型參數聲明上**。 (2)in關鍵字修飾泛型類或者泛型接口中的泛型參數時會**支持逆變**。 (3)**泛型參數T在使用了in關鍵字之后,不能聲明成val或者var類型的變量**。 ### 點變型(聲明點變型和使用點變型) 前幾個小節中說到的out、in關鍵字都是出現在類或者接口中的泛型參數聲明的時候,這樣做確實比較方便,因為它們的作用范圍比較廣,可以應用到所有類被使用的地方。這種**把out、in關鍵字放在泛型參數聲明處的情況被稱為聲明點變型**。需要注意的是,**如果泛型參數中使用了var類型變量,則此處無法使用out、in關鍵字,也就不能聲明點變型**。 **除了在類或接口中定義泛型參數時使用out、in關鍵字之外,還可以在泛型參數出現的具體位置使用out、in關鍵字**,這種變型被稱為**使用點變型**。接下來我們通過一個案例來解釋使用點變型。具體代碼如下所示。 ``` open class Fruit(val name: String) open class Mammal(val name: String) class Banana : Fruit("香蕉") class Pear : Fruit("梨子") class Lion : Mammal("獅子") class Tiger : Mammal("老虎") class Forest<T>(var content: T) //打印Box中的Fruit的name fun printFruit(forest: Forest<out Fruit>) { println(forest.content.name) } //打印Box中的Animal的name fun printMammal(forest: Forest<out Mammal>) { println(forest.content.name) } fun main(args: Array<String>) { val bananaForest = Forest<Banana>(Banana()) val pearForest = Forest<Pear>(Pear()) val lionForest = Forest<Lion>(Lion()) val tigerForest = Forest<Tiger>(Tiger()) printFruit(bananaForest) printFruit(pearForest) printMammal(lionForest) printMammal(tigerForest) } ``` 運行結果: ``` 香蕉 梨子 獅子 老虎 ``` 上述代碼中,在第7行代碼中定義泛型Forest時,沒有使用關鍵字out、in。由于在定義泛型Forest時,泛型參數中使用了var類型變量,因此也無法在此處使用out、in關鍵字,也就沒有聲明點變型。 在上述代碼的**第8和12行中,在泛型參數出現的位置使用了out關鍵字,這個關鍵字使泛型參數進行了協變**。在printFruit()方法中傳遞的形參是`Forest<out Fruit>`類型,在第17和18行代碼中向該方法中傳遞的是`Forest<Banana>`、`Forest<Pear>`類型,由于這兩種類型都是`Forest<out Fruit>`的子類型,因此傳遞這兩個類型都是可行的。同樣,由于`Forest<Lion>`、`Forest<Tiger>`都是`Forest<out Mammal>`子類型,因此在printMammal()方法中傳遞這兩個泛型類型的參數也是可以的。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看