Kotlin開發筆記:集合和逆變協變

Kotlin開發筆記:集合和逆變協變

在這里插入圖片描述

Kotlin中的集合

基本的集合類型

Kotlin中的集合類型和Java差不多,不過有些在名稱上可能有出入,下面是Kotlin中的一些基本集合類型:

類型介紹
Pair兩個值的元組
Triple三個值的元組
Array經過索引的,固定大小的對象和基元集合
List有序的對象集合
Set無序的對象集合
Map鍵值對對象集合

Kotlin中的視圖

在Kotlin引入了視圖的概念,簡而言之,不同的視圖類型會賦予我們對操作集合的不同權限。Kotlin中有兩種不同的視圖:只讀或不可變視圖,以及讀寫或可變視圖

比如對于List來說,有兩種視圖,分別是List和MutableList,前者提供只讀視圖,后者提供讀寫視圖。當我們用List視圖時將無法修改列表,而用MutableList就可以。

    var li = listOf(1,2,3) as MutableListli.add(5)

比如我們運行上述代碼就會報錯,因為listOf函數會產生List視圖的集合,這將導致我們無法修改列表,如果我們要續寫就需要產生讀寫視圖的列表:

    var li = mutableListOf(1,2,3) li.add(5)

不過本質上這兩種視圖都是對List的引用,比如說我們可以用List視圖引用同一個ArrayList:

    val ar = arrayListOf(1,2,3,4)val li:List<Int> = arval li1:MutableList<Int> = ar

不過List視圖的引用將無法修改列表本身。

Kotlin中的一些技巧

使用listOf等函數快速創建集合

這個其實在上面給的例子里已經體現了,我們可以使用arrayListOf,listOf等函數快速創建出我們想要的集合而無需再用構造函數。

使用to和mapOf快速創建表

Kotlin中提供了一個to拓展函數,這個函數將生成一個Pair類型的對象,比如

val p1 = "age" to 18

將會創建一個First為"age",Second為18的Pair對象。而這個對象又可以用于mapOf函數。這樣我們就可以快速創建一個map,比如:

val mMap = mapOf("age" to 18,"code" to 10086)

這樣就創建了一個鍵值對為< String , Int >類型的map,其中to之前的為Key,之后的為Value。

同時獲取索引和值

在Java中,如果我們想要同時獲取一個List的索引和值的話可能需要遍歷或者采取別的手段來達到這個目的,而在Kotlin中,我們可以用解構來實現這個目的:

fun main() {val li = listOf("jack","anderson")for((index,value) in li.withIndex()){println("index : $index, value:$value")}
}

withIndex將返回一個包含鍵值對的對象,我們將其解構出來就可以同時獲得索引和值了。

創建有規律的數組

接下來介紹的是如何創建出一個有規律的數字,比如我們可以創建出一個物的倍數的數組:

fun main() {val li = Array(5){index -> index * 5}for(value in li){println(value)}
}

Array括號后面的5是元素個數,index下標是從0開始,我們可以打印出值:
在這里插入圖片描述
成功創建了一個包含五的倍數的數組,利用這個技巧我們再加上Array內置的一些方法,就可以實現許多計算,比如我們想要計算從1到5的平方和的話就可以直接這樣寫:

fun main() {val li = Array(5){index -> (index+1) * (index+1)}.sum()println(li)
}

使用in

在Java中如果我們想要判斷一個元素是否在一個集合中,一般會使用contains方法,不過在Kotlin中提供了in運算符實現了同樣的效果:

fun main() {val li = Array(5){index -> index*5}println(0 in li)
}

實際上在迭代時我們會用到in運算符也是這樣。

Kotlin中的逆變和協變

什么是逆變和協變

首先我們需要介紹逆變和協變的概念,協變和逆變都是術語,前者指能夠使用比原始指定的派生類型的派生程度更大(更具體的)的類型,后者指能夠使用比原始指定的派生類型的派生程度更小(不太具體的)的類型。

以我的理解,協變應該接近于extend,而逆變接近于super。

默認情況下,在Java中泛型強制實行類型不變性–也就是說,如果泛型函數期望一個參數類型T,則不允許替換基類型T或者派生類型T,類型必須是完全預期的類型

實際上,在Java中我也沒有對通配符和一般的泛型T的區別和相同有什么很深的理解。我的理解是,通配符?代表不確定的類型,泛型類型T代表確定的類型

類型不變性

這里再介紹一下類型不變性,當一個方法接收到一個類型為T的對象(確定對象,不是泛型對象)時,我們可以傳入為T類型或者是T的子類的對象。比如如果一個方法接收一個Animal類型的對象,那么身為Animal子類的Cat類型的對象也可以被傳進去。

但是,如果這個方法接收的是一個泛型類型為T的對象,那么將不允許傳遞派生類型為T的泛型對象。比如,如果可以傳遞List< Animal >類型的對象,那么將不允許傳入List< Cat >類型的對象,這和Java中的類型擦除有關。

書上的一個例子我覺得很形象,比如說我們創建一個Fruit類和兩個繼承它的類還有一個接收水果的方法:

open class Fruit
class Orange:Fruit()
class Banana:Fruit()fun receiveFruits(fruits:Array<Fruit>){println("水果的數量是${fruits.size}")
}

這個方法可以接受泛型類型為Fruit的數組,如果我們傳入Orange或者Banana會怎么樣呢?
在這里插入圖片描述
可以看到,編譯器提示類型不匹配了。香蕉是從水果繼承而來的,但是顯然一籃子香蕉不是從一籃子水果繼承而來的

不過一旦我們用list視圖來操作,上述代碼就不會報錯了:
在這里插入圖片描述
這是因為List視圖只允許我們進行讀而不允許我們進行寫,這樣是安全的。在Kotlin中,這個效果是由于List視圖是out修飾的,我們將在后面的協變中介紹。
在這里插入圖片描述

使用協變

上邊介紹到了,一旦我們使用List視圖,那么receiveFruits方法就可以被調用了,這正是由于使用了協變的原因。接下來我們創建一個方法來模擬協變的使用場景,比如我們想要把一個Fruit的Array復制到另一個Fruit的Array中:

fun copyFromTo(from:Array<Fruit>,to:Array<Fruit>){for(i in 0 until from.size){to[i] = from[i]}
}

這種情況下我們顯然不能傳入除Fruit類之外的泛型類,比如:
在這里插入圖片描述
編譯器是不會允許我們傳入泛型類型為Banana的參數給from的,這個時候我們只需要修改一下這個函數,在傳入的from參數處使用協變即可:

fun copyFromTo(from:Array<out Fruit>,to:Array<Fruit>){for(i in 0 until from.size){to[i] = from[i]}
}

在這里插入圖片描述
這樣編譯器就不會報錯了。要理解這個協變的含義我們可以從編譯器為什么不讓我們傳入Banana類型的參數看。如果我們可以傳入Banana類型的參數,我們就有可能對Banana執行一些Fruit層面的指令。

舉個例子來說,大部分水果沖洗完成之后就可以直接食用了,但是香蕉的果皮較厚,我們就不能直接食用,在這之前還需要剝皮。身為子類的Banana🍌肯定是有其特殊之處的,不能用基類Fruit的一些操作直接用在Banana上。但是如果我們不對這個Banana進行操作的話,那么就不會有什么大問題了,這就是協變的含義。

這里對copyFromTo方法的from參數加上out參數后就說明我們不會對這個from參數進行任何方法的調用了,我們只是單單讀取這個參數,這樣編譯器就允許我們傳入Fruit的子類的泛型類型了,換言之,我們就實現了協變。這種在使用泛型類型時使用協變的行為稱之為“使用點型變”。

使用逆變

與協變相對的就是逆變了,如果說協變是只讀不寫的話,那么逆變就是只寫不讀。實際上也確實是這樣,使用逆變將允許我們在該參數上進行設置值的方法調用,而不允許讀取的方法。

我們依舊以上面的copyFromTo方法為例,現在我們希望可以將任意Fruit或者Fruit子類的元素復制到Fruit或Fruit超類的集合中,比如說我們傳一個Any類的參數:
在這里插入圖片描述
顯然由于類型不變性這樣是行不通的,在這里我們再次對copyFromTo方法做修改,這次我們對to參數使用逆變:

fun copyFromTo(from:Array<out Fruit>,to:Array<in Fruit>){for(i in 0 until from.size){to[i] = from[i]}
}

在這里插入圖片描述
這樣編譯器就允許我們這樣調用了。

使用Where的參數類型約束

這部分內容說白了就是約束泛型類型的范圍,比如說我們有一個方法需要傳入一個泛型類,這個泛型類需要實現AutoCloseable接口,那么我們就可以這樣寫

fun <T:AutoCloseable> useAndClose(input:T)
{input.close()
}

實際上上面的和Java中的寫法也差不多,不過如果是一個泛型需要實現多個接口的話就不能這么寫了,需要我們用where參數進行約束:

fun <T> useAndClose(input:T)where T:AutoCloseable,T:Appendable
{input.append("haha")input.close()
}

where約束跟在參數列表后面,花括號前面。約束參數中用逗號分隔。

星投影

星投影用<*>定義參數類型,它是指定泛型只讀類型和原始類型的Kotlin等效物,**簡單來說,我們可以用星投影捕獲泛型類型,但是我們只能對捕獲的泛型類型進行讀取而不能修改。**當你想表達對類型不太了解但有希望類型安全時,請使用星投影,星投影只允許讀出而不允許寫入,比如:

fun printValues(values:Array<*>){for(value in values){println(value)}
}

在這個方法中我們用星投影捕獲了泛型類型,但是我們只能讀取values值不能寫入或者更改values值,實際上就相當于out T,但是寫起來更簡潔。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/42564.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42564.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42564.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

去掉鼠標系列之一: 語雀快捷鍵使用指南

其實應該是系列之二了&#xff0c;因為前面寫了一個關于Interlij IDEA的快捷鍵了。 為什么要寫這個了&#xff0c;主要是覺得一會兒用鼠標&#xff0c;一會兒鍵盤&#xff0c;一點兒不酷&#xff0c;我希望可以一直用鍵盤&#xff0c;拋開鼠標。后面陸續記錄一下各個軟件的快捷…

高效使用ChatGPT之ChatGPT客戶端

ChatGPT客戶端&#xff0c;支持Mac, Windows, and Linux 下載地址見文章結尾 軟件截圖 Windows: Mac&#xff1a; 說明 chatgpt桌面版&#xff0c;相比于網頁版的chatgpt&#xff0c;最大的特色是支持歷史聊天對話記錄導出&#xff0c;且支持三種格式&#xff1a;PNG、PDF、…

由淺入深詳解四種分布式鎖

在多線程環境下&#xff0c;為了保證數據的線程安全&#xff0c;鎖保證同一時刻&#xff0c;只有一個可以訪問和更新共享數據。在單機系統我們可以使用synchronized鎖或者Lock鎖保證線程安全。synchronized鎖是Java提供的一種內置鎖&#xff0c;在單個JVM進程中提供線程之間的鎖…

小程序的數據綁定和事件綁定

小程序的數據綁定 1.需要渲染的數據放在index.js中的data里 Page({data: {info:HELLO WORLD,imgSrc:/images/1.jpg,randomNum:Math.random()*10,randomNum1:Math.random().toFixed(2)}, }) 2.在WXML中通過{{}}獲取數據 <view>{{info}}</view><image src"{{…

C++ std::thread

若要使用線程類std::thread&#xff0c;則需包含<thread>頭文件。 創建線程 std::thread表示一個線程。線程對象是不可復制或賦值的&#xff0c;但可以移動(move)&#xff0c;如移動構造或移動賦值。 當構造std::thread對象時&#xff0c;需給構造函數輸入一個參數&am…

RocketMQ 5.0 架構解析:如何基于云原生架構支撐多元化場景

作者&#xff1a;隆基 本文將從技術角度了解 RocketMQ 的云原生架構&#xff0c;了解 RocketMQ 如何基于一套統一的架構支撐多元化的場景。 文章主要包含三部分內容。首先介紹 RocketMQ 5.0 的核心概念和架構概覽&#xff1b;然后從集群角度出發&#xff0c;從宏觀視角學習 R…

swift 項目集成友盟推送

1, 需要用橋接文件 , 不然引用不到依賴庫 2, 可以用測試模式測試, 可以debug 3, 測試模式獲取deviceToken, 添加測試設備 deviceToken獲取方法 func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { le…

spark使用心得

spark入門 啟停spark sbin/start-all.shsbin/stop-all.shspark-shell 進入spark/bin目錄&#xff0c;執行&#xff1a; ./spark-shell 輸出中有這么一行&#xff1a; Spark context Web UI available at http://xx.xx.xx.188:4040意味著我們可以從web頁面查看spark的運行情…

優測云服務平臺|【壓力測試功能升級】輕松完成壓測任務

一、本次升級主要功能如下&#xff1a; 1.多份報告對比查看測試結果 2.報告新增多種下載格式 Word格式Excel格式 3.新增多種編排復雜場景的控制器 漏斗控制器并行控制器事務控制器僅一次控制器分組控制器集合點 4.新增概覽頁面&#xff0c;包含多種統計維度 二、報告對比…

開源語音聊天軟件Mumble

網友 大氣 告訴我&#xff0c;Openblocks在國內還有個版本叫 碼匠&#xff0c;更貼合國內軟件開發的需求&#xff0c;如接入了國內常用的身份認證&#xff0c;接入了國內的數據庫和云服務&#xff0c;也對小程序、企微 sdk 等場景做了適配。 在 https://majiang.co/docs/docke…

類與對象(上)

類與對象&#xff08;上&#xff09; 一、面向過程和面向對象的區別二、類1、類的引入2、類的定義&#xff08;1&#xff09;類的基本定義&#xff08;2&#xff09;類的成員函數的定義方法 3、類的訪問限定符4、封裝5、駝峰法命名規則6、類的作用域7、類的實例化&#xff08;1…

金蝶軟件實現導入Excel數據分錄行信息到單據體分錄行中

>>>適合KIS云專業版V16.0|KIS云旗艦版V7.0|K/3 WISE 14.0等版本<<< 金蝶軟件中實現[導入Excel數據業務分錄行]信息到[金蝶單據體分錄]中,在采購訂單|采購入庫單|銷售訂單|銷售出庫單等類型單據中,以少量的必要字段在excel表格中按模板填列好,很方便快捷地從…

IntelliJ IDEA(簡稱Idea) 基本常用設置及Maven部署---詳細介紹

一&#xff0c;Idea是什么&#xff1f; 前言&#xff1a; 眾所周知&#xff0c;現在有許多編譯工具&#xff0c;如eclipse&#xff0c;pathon, 今天所要學的Idea編譯工具 Idea是JetBrains公司開發的一款強大的集成開發環境&#xff08;IDE&#xff09;&#xff0c;主要用于Java…

Rancher管理K8S

1 介紹 Rancher是一個開源的企業級多集群Kubernetes管理平臺&#xff0c;實現了Kubernetes集群在混合云本地數據中心的集中部署與管理&#xff0c;以確保集群的安全性&#xff0c;加速企業數字化轉型。Rancher 1.0版本在2016年就已發布&#xff0c;時至今日&#xff0c;Ranche…

2023牛客第七場補題報告C F L M

2023牛客第七場補題報告C F L M C-Beautiful Sequence_2023牛客暑期多校訓練營7 (nowcoder.com) 思路 觀察到數組一定是遞增的&#xff0c;所以從最高位往下考慮每位的1最多只有一個&#xff0c;然后按位枚舉貪心即可。 代碼 #include <bits/stdc.h> using namespac…

CS:GO升級 Linux不再是“法外之地”

在前天的VAC大規模封禁中&#xff0c;有不少Linux平臺的作弊玩家也迎來了“遲到”的VAC封禁。   一直以來&#xff0c;Linux就是VAC封禁的法外之地。雖然大部分玩家都使用Windows平臺進行游戲。但實際上&#xff0c;使用Linux暢玩CS:GO的玩家也不在少數。 以前V社主要打擊W…

Linux上安裝和使用git到gitoschina和github上_親測

Linux上安裝和使用git到gitoschina和github上_親測 git介紹與在linux上安裝創建SSHkey在git-oschina使用maven-oschina使用在github使用maven-github使用組織與倉庫 【git介紹與在linux上安裝】 Git是一款免費、開源的分布式版本控制系統&#xff0c;用于敏捷高效地處理任何…

uniapp隱藏底部導航欄(非自定義底部導航欄)

uniapp隱藏底部導航欄 看什么看&#xff0c;要多看uni官方文檔&#xff0c;里面啥都有 看什么看&#xff0c;要多看uni官方文檔&#xff0c;里面啥都有 uniapp官方網址&#xff1a;uni設置TabBar // 展示 uni.showTabBar({animation:true,success() {console.debug(隱藏成功)…

【LVS】1、LVS負載均衡群集

1.群集的含義&#xff1a; Cluster、群集、集群 由多臺主機構成并作為一個整體&#xff0c;只提供一個訪問入口&#xff08;域名與IP地址&#xff09;&#xff1b;可伸縮 2.集群使用的場景&#xff1a; 高并發 3.企業群集的分類&#xff1a; 根據群集所針對的目標差異&a…

06-微信小程序-注冊程序-場景值

06-微信小程序-注冊程序 文章目錄 注冊小程序參數 Object object案例代碼 場景值場景值作用場景值列表案例代碼 注冊小程序 每個小程序都需要在 app.js 中調用 App 方法注冊小程序實例&#xff0c;綁定生命周期回調函數、錯誤監聽和頁面不存在監聽函數等。 詳細的參數含義和使…