kotlin语言中的out和in
在 kotlin 語言中,out 表示協(xié)變,in 表示逆變;協(xié)變和逆變并不是 kotlin 獨(dú)有的概念,像 Java、C#都有這樣的概念;為了能夠理解 kotlin 語言中的 out 和 in,我們先用 Java 的泛型來舉例,我們需要用泛型,是因?yàn)樗暮锰幘褪窃诰幾g的時(shí)候能夠檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的。
1、Java 中的 ? extends T 和 ? super T
1、1 ? extends T
ps:代碼是在 AndroidStudio 工具上寫的
建立一個(gè) Java 文件鳥類 Birds;
public class Birds { private String name; public Birds(String name) { this.name = name; } public void flight() { System.out.println("我是" + name + ",屬于鳥類,我能飛行"); } }建立一個(gè) Java 文件烏鴉類 Crow 并繼承 Birds;
public class Crow extends Birds { public Crow(String name) { super(name); } }新建一個(gè) Java 文件的泛型類 TestBirds 并限制泛型 T 是 Birds 的子類;
public class TestBirds<T extends Birds> { public void actionBirds(List<T> birds) { for (T bird : birds) { bird.flight(); } } }在程序入口嘗試使用 List<Crow> list 作為參數(shù)傳遞給 TestBirds 的 actionBirds 方法;
List<Crow> list = new ArrayList<>(); TestBirds<Birds> testBirds = new TestBirds<>(); Crow crow = new Crow("烏鴉"); list.add(crow); /** * 這里 list 地方會(huì)編譯報(bào)紅線 */ testBirds.actionBirds(list);這時(shí)候傳入 list 發(fā)現(xiàn)編譯不通過,我們這里分析一下:TestBirds 是泛型類,沒有使用的之前,T 是不確定的,使用之后 T 是確定的,它是 Birds;把 list 作為參數(shù)傳入 actionBirds 方法中,等同于 List<Birds> birds = list,但是 List<Birds> birds = list 是不成立的,雖然 Crow 繼承于 Birds,birds 只保存的是 Birds 類型的對(duì)象,list 只保存 Crow 類型的對(duì)象,birds 和 list 是沒有任何關(guān)系的。
TestBirds 類中新添加一個(gè) actionBirds2 方法,該方法是在 actionBirds 方法的基礎(chǔ)上修改的;
public void actionBirds2(List<? extends T> birds) { for (T bird : birds) { bird.flight(); } }在程序入口使用 List<Crow> list 作為參數(shù)傳遞給 TestBirds 的 actionBirds2 方法;
List<Crow> list = new ArrayList<>(); TestBirds<Birds> testBirds = new TestBirds<>(); Crow crow = new Crow("烏鴉"); list.add(crow); /** * 這里 list 地方會(huì)編譯報(bào)紅線 */ // testBirds.actionBirds(list); testBirds.actionBirds2(list);這時(shí)候發(fā)現(xiàn) testBirds.actionBirds2(list) 這行代碼編譯通過了,是不是感覺很神奇?這里分析一下:把 list 作為參數(shù)傳入 actionBirds2 方法相等于 List<? extends Birds> birds = list,它是成立的,List<? extends Birds> birds ,表示集合存儲(chǔ)的是 Birds 和 Birds 的子類對(duì)象,限定了上屆,而 list 存儲(chǔ)的是 Birds 子類的對(duì)象,所以代碼編譯通過,它是成立的;上界通配符 < ? extends T>,用 extends 關(guān)鍵字聲明,表示參數(shù)可能是T,或者是T的子類。
1、2 ? super T
在上面原有代碼的基礎(chǔ)上,在 TestBirds 類中添加一個(gè) actionBirds3 方法;
public void actionBirds3(List<T> birds,List<T> crows) { for (T crow : crows) { birds.add(crow); } }在程序入口嘗試調(diào)用 TestBirds 的 actionBirds3 方法;
List<Crow> list = new ArrayList<>(); TestBirds<Crow> testBirds = new TestBirds<>(); Crow crow = new Crow("烏鴉"); list.add(crow); List<Birds> birdsList = new ArrayList<>(); testBirds.actionBirds3(birdsList,list);到這里后,發(fā)現(xiàn) actionBirds3 方法的第一個(gè)參數(shù)就報(bào)紅色編譯錯(cuò)誤了,原因是實(shí)例化 TestBirds 類對(duì)象的時(shí)候,T 被 Crow 替換了,birdsList 只存儲(chǔ) Birds 類型的數(shù)據(jù),而 actionBirds3 方法的第一個(gè)參數(shù)只存儲(chǔ) Crow 類型的數(shù)據(jù),所以2者沒有任何關(guān)系,所以語法編譯錯(cuò)誤。
我們?cè)?TestBirds 中新添加一個(gè) actionBirds4 方法,在 actionBirds3 的基礎(chǔ)上改動(dòng)一下第一個(gè)參數(shù);
public void actionBirds4(List<? super T> birds,List<T> crows) { for (T crow : crows) { birds.add(crow); } }在實(shí)參不變的情況下,在程序入口調(diào)用 TestBirds 的 actionBirds4 方法;
List<Crow> list = new ArrayList<>(); TestBirds<Crow> testBirds = new TestBirds<>(); Crow crow = new Crow("烏鴉"); list.add(crow); List<Birds> birdsList = new ArrayList<>(); /** * 這里 birds 地方會(huì)編譯報(bào)紅線 */ // testBirds.actionBirds3(birds,list); testBirds.actionBirds4(birdsList,list);這時(shí)候發(fā)現(xiàn)調(diào)用 TestBirds 中的 actionBirds4 方法編譯通過,分析一下:actionBirds4 方法的第一個(gè)參數(shù)為 List<? super T> birds,? super T 是下限通配符,它表示在使用中限定參數(shù)類型為 T 或者是 T 的父類,birds 存儲(chǔ)的是 T 類型和 T 父類的對(duì)象;在實(shí)例化 TestBirds 的過程中,把 Crow 替換成了 T,Birds 剛好是 Crow 的父類,調(diào)用 TestBirds 的 actionBirds4 方法傳遞第一個(gè)參數(shù)時(shí)相當(dāng)于 List<? super Crow> birds = list,所以編譯通過。
2、kotlin 中的 out 和 in
2、1 out
在上面 ? extends T 的代碼案例中,TestBirds 類中的 actionBirds2 方法的第一個(gè)參數(shù) birds 集合加了 ? extends T 進(jìn)行限制,然后用 for 循環(huán)遍歷 T 的元素進(jìn)行取出來,這樣的操作是讀取;? extends T 限定了通配符類型的上界,所以我們可以安全地從其中讀取卻不可以修改數(shù)據(jù);我們可以把那些只能從中讀取的對(duì)象稱為生產(chǎn)者;List<? extends T> 這樣的類型不進(jìn)行消費(fèi)的生產(chǎn)者,以保證類型運(yùn)行的安全,這就是協(xié)變;在 kotlin 中用 out 表示,kotlin 中的 “out T” 等同于 Java 的 “?extends T”;下面用 kotlin 的 out 關(guān)鍵字舉個(gè)例子:
新建一個(gè) kotlin 類 TestBirds2 并寫一個(gè)和 TestBirds 類 actionBirds2 效果一樣的函數(shù);
class TestBirds2<T: Birds> { fun actionBirds2(birds: MutableList<out T>) { for (bird: T in birds) { bird.flight() } } }在程序入口調(diào)用 TestBirds2 中的 actionBirds2 函數(shù);
var testBirds2: TestBirds2<Birds> = TestBirds2<Birds>() var crow: Crow = Crow("烏鴉") var crowList: MutableList<Crow> = mutableListOf(crow) testBirds2.actionBirds2(crowList)2、2 in
在上面 ? super T 的代碼案例中,TestBirds 類中的 actionBirds4 方法的第一個(gè)參數(shù) birds 集合加了 ? super T 進(jìn)行限制,然后用 for 循環(huán)遍歷第二個(gè)參數(shù)crows 的 T 元素進(jìn)行取出來,再將 T 元素放入到 birds 集合中;? super T 限定了通配符類型的下界,所以我們可以安全地從其中修改數(shù)據(jù),也就是將 T 元素放入到 birds 集合中;我們可以把那些只能從中修改的對(duì)象稱為消費(fèi)者;List<? super T> 這樣的類型獲取出來的數(shù)據(jù)類型是 Object,沒有意義,可認(rèn)為不進(jìn)行生產(chǎn)的消費(fèi)者,以保證類型運(yùn)行的安全,這就是逆變;在 kotlin 中用 in 表示,kotlin 中的 “in T” 等同于 Java 的 “?super T”;下面用 kotlin 的 in 關(guān)鍵字舉個(gè)例子:
在 TestBirds2 類中寫一個(gè)和 TestBirds 類中 actionBirds4 方法效果一樣的函數(shù);
fun actionBirds4(birds: MutableList<in T>,crow: MutableList<T>) { for (t: T in crow) { birds.add(t) } }在程序入口調(diào)用 TestBirds2 中的 actionBirds4函數(shù);
var testBirds2: TestBirds2<Crow> = TestBirds2<Crow>() var crow: Crow = Crow("烏鴉") var crowList: MutableList<Crow> = mutableListOf(crow) var birdsList: MutableList<Birds> = mutableListOf() testBirds2.actionBirds4(birdsList,crowList)2、3 類型投影
上面的 out 和 in 的例子使用起來還是有限制,因?yàn)橛?T 繼承于 Birds 的局限;這里講一下類型投影,在講類型投影之前先說一下 Any,Any 是 kotlin 語言的祖宗類,類似于 Java 中的 Object,但是又不是等于 Object,因?yàn)?Any 只有 equals、hashCode 和 toString 這3個(gè)函數(shù);將一個(gè)類聲明為泛型類,泛型類型可以出現(xiàn)在 out 位置,也可以出現(xiàn)在 in 位置,我們就可以在使用處將其聲明成協(xié)變或者逆變,就等于把這個(gè)類型投影出某一面進(jìn)行使用,就屬于類型投影;就拿泛型類 MutableList<T> 來說,真正要實(shí)例化 MutableList 的時(shí)候,T 的位置可以多添加 in 或者 out, 把這個(gè)類型投影出某一面進(jìn)行使用,也就是 MutableList 的讀取數(shù)據(jù)方法 get 或者寫入數(shù)據(jù)方法 add;下面就拿 MutableList 寫代碼舉例一下:
var mutableList: MutableList<out Any> = mutableListOf("公眾號(hào)小二玩編程",2,3,4,5) var size: Int = mutableList.size - 1 var any: Any? = null for (i: Int in 0 .. size) { any = mutableList.get(i) println("第" + (i + 1) + "any是--" + any) } var mutableList2: MutableList<in String> = mutableListOf() mutableList2.add("公眾號(hào)小二玩編程") /** * 這里 Int 類型,編譯會(huì)報(bào)錯(cuò),因?yàn)?mutableList2 做了 in String 限制 */ mutableList2.add(2)本篇文章寫到這里就結(jié)束了,由于技術(shù)水平有限,文章中難免會(huì)有錯(cuò)誤,歡迎大家批評(píng)指正。
總結(jié)
以上是生活随笔為你收集整理的kotlin语言中的out和in的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【计算机毕设之基于python的股票价格
- 下一篇: supersqli 攻防世界