網(wǎng)站首頁 編程語言 正文
1.泛型
Android項(xiàng)目開發(fā)過程中普遍會(huì)遇到一個(gè)問題:adapter
的樣式、業(yè)務(wù)邏輯很相似,但是需要的數(shù)據(jù)源不是來自一個(gè)接口,常規(guī)情況下就要定義多個(gè)構(gòu)造函數(shù)但是這樣就要更改構(gòu)造函數(shù)的傳參順序或者增加傳參要么就是將他們統(tǒng)一成一個(gè)類。但是用泛型就可以這樣解決:
class CommonAdapter<T>(val list: List<T>) : RecyclerView.Adapter<CommonAdapter.ViewHolder>() { class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { TODO("Not yet implemented") } override fun onBindViewHolder(holder: ViewHolder, position: Int) { TODO("Not yet implemented") } override fun getItemCount() = list.size } //調(diào)用CommonAdapter mBinding.rvTab.adapter = CommonAdapter(listOf("周一","周二","周三")) mBinding.rvTab.adapter = CommonAdapter(listOf(CommonData("張三"),CommonData("李四"),CommonData("王五")))
可以看到泛型可以很好的解決這個(gè)問題。
再舉個(gè)例子,電子產(chǎn)品的充電器,在以前充電接口沒有統(tǒng)一的時(shí)候每個(gè)設(shè)備都需要一個(gè)單獨(dú)的充電器,這樣就很麻煩,下面的代碼就是對(duì)這個(gè)問題的一種表示
// 手機(jī) class Phone { fun charging() {} } // 平板 class Pad { fun charging() {} } //無線耳機(jī) class Headset { fun charging() {} } //便攜音箱 class Speakers { fun charging() {} }
統(tǒng)一充電器接口后就只需要保留一個(gè)充電器即可,這個(gè)概念就需要用到泛型了
//統(tǒng)一接口 class UnifiedInterface<T> { fun charging(device: T) {} } val phone = UnifiedInterface<Phone>() phone.charging() val pad = UnifiedInterface<Pad>() pad.charging()
在統(tǒng)一接口UnifiedInterface
中傳入要充電的電子設(shè)備就可以了。T
代表的就是各種電子設(shè)備。
這里要注意的一點(diǎn)是在使用泛型的時(shí)候還可以加上邊界
class UnifiedInterface<T: Phone> { fun charging(device: T) {} }
上面的代碼中Phone
就是邊界,用【:】這個(gè)邊界的聲明就是說只能傳入Phone
類或者它的子類,傳入Pad
或者Headset
都是不可以的。
2.型變
fun main() { func(mutableListOf<Phone>(Phone())) //報(bào)錯(cuò) 這里應(yīng)該傳入Device類型的集合 } fun func(list: MutableList<Device>) { list.add(Pad()) } open class Device { } class Phone : Device() { } class Pad { }
這里定義了一個(gè)方法,方法中的傳參是Device
類型的集合,調(diào)用的時(shí)候傳入的Phone
類型的集合,而Device
和Phone
是繼承關(guān)系但是卻無法傳入Phone
類型的集合。這是因?yàn)樵谀J(rèn)情況下MutableList<Device>
和MutableList<Phone>
之間不存在任何繼承關(guān)系,他們也無法互相替代,這就是泛型的不變性。
那么什么是型變?型變就是為了解決泛型的不變性問題。
3.型變—逆變
以手機(jī)為例,Android
是手機(jī)類,還有XioMi
和HuaWei
兩個(gè)子類,它倆和Android
是繼承關(guān)系,那么Charger<XiaoMi>
、Charger<HuaWei>
和Charger<Android>
之間有什么關(guān)系?
class Charger<T> { fun charging(device: T) {} } open class Android { open fun charging() {} } class XiaoMi : Android() { override fun charging() { } } class HuaWei() : Android() { override fun charging() { super.charging() } }
假設(shè),現(xiàn)在手機(jī)都沒電了,需要用充電器充電,那么給XiaoMi手機(jī)充電就是這樣
fun xiaoMiCharger(charger: Charger<XiaoMi>) { val xiaomi = XiaoMi() charger.charging(xiaomi) }
但是還有一個(gè)HuaWei手機(jī)也要充電,是否可以用一個(gè)充電器?就像下面這樣
fun main() { val charger = Charger<Android>() xiaoMiCharger(charger) //報(bào)錯(cuò):類型不匹配 huaWeiCharger(charger) //報(bào)錯(cuò):類型不匹配 }
都是Android手機(jī)為什么不能充電?這主要是編譯器不認(rèn)為XiaoMi和HuaWei是Android手機(jī),也就是說它們?nèi)咧g沒有關(guān)系,這就是上面講的不可變性, 此時(shí)逆變踩著歡快的腳步到來了。
- 使用處型變
// 修改處 // ↓ fun xiaoMiCharger(charger: Charger<in XiaoMi>) { val xiaomi = XiaoMi() charger.charging(xiaomi) } // 修改處 // ↓ fun huaWeiCharger(charger: Charger<in HuaWei>) { val huaWei = HuaWei() charger.charging(huaWei) } class Charger<T> { fun charging(device: T) {} } fun main() { val charger = Charger<Android>() xiaoMiCharger(charger) huaWeiCharger(charger) }
- 聲明處型變
// 修改處 // ↓ class Charger<in T> { fun charging(device: T) {} } fun xiaoMiCharger(charger: Charger<XiaoMi>) { val xiaomi = XiaoMi() charger.charging(xiaomi) } fun huaWeiCharger(charger: Charger<HuaWei>) { val huaWei = HuaWei() charger.charging(huaWei) } fun main() { val charger = Charger<Android>() xiaoMiCharger(charger) huaWeiCharger(charger) }
加上in
關(guān)鍵字之后就報(bào)錯(cuò)就消失了
為什么被叫做逆變?
上面的代碼其實(shí)是將父子關(guān)系顛倒了,以使用處型變?yōu)槔覀儼汛a放到一起看看
fun main() { val charger = Charger<Android>() xiaoMiCharger(charger) huaWeiCharger(charger) } //Charger<Android> → Charger<XiaoMi> 成了顛倒關(guān)系 fun xiaoMiCharger(charger: Charger<in XiaoMi>) { val xiaomi = XiaoMi() charger.charging(xiaomi) } fun huaWeiCharger(charger: Charger<in HuaWei>) { val huaWei = HuaWei() charger.charging(huaWei) }
這種父子顛倒的關(guān)系被稱為逆變。
4.型變—協(xié)變
假設(shè)我要去商場(chǎng)買一個(gè)Android手機(jī),它屬于Phone類
open class Phone { } class Android : Phone() { } //商場(chǎng)什么都賣 class Shop<T> { fun buy(): T { TODO("Not yet implemented") } } //去商場(chǎng)買手機(jī) fun buy(shop: Shop<Phone>) { val phone = shop.buy() } fun buy(shop: Shop<Phone>) { val phone = shop.buy() } fun main() { val android = Shop<Android>() buy(android) //報(bào)錯(cuò)了,類型不匹配 }
Android是Phone的子類,但是Shop<Android>
和Shop<Phone>
卻沒有關(guān)系,這依舊是Kotlin的不可變性,前面講過通過in
實(shí)現(xiàn)逆變, 但是它的父子關(guān)系就被顛倒了,那么這里的目的就是維持正確的父子關(guān)系——協(xié)變。
- 使用處協(xié)變
class Shop<T> { fun buy(): T { TODO("Not yet implemented") } } // 修改處 // ↓ fun buy(shop: Shop<out Phone>) { val phone = shop.buy() } fun main() { val android = Shop<Android>() buy(android) //報(bào)錯(cuò)消失 }
- 聲明處協(xié)變
// 修改處 // ↓ class Shop<out T> { fun buy(): T { TODO("Not yet implemented") } } fun buy(shop: Shop<Phone>) { val phone = shop.buy() } fun main() { val android = Shop<Android>() buy(android) //報(bào)錯(cuò)消失 }
通過out
就實(shí)現(xiàn)了協(xié)變, 父子關(guān)系也沒有顛倒,關(guān)系圖如下
Kotlin的型變的逆變、協(xié)變到這里就講完了,Java中也有型變,但是只有使用處沒有聲明處
Kotlin | Java | |
---|---|---|
逆變 | Charger | Charger<? super XiaoMi> |
協(xié)變 | Shop | Shop<? extends Phone> |
//RxJava#ObservableAnySingle public ObservableAnySingle(ObservableSource<T> source, Predicate<? super T> predicate) { this.source = source; this.predicate = predicate; }
//String#join public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }
逆變和協(xié)變有什么區(qū)別?怎么用?
//聲明處逆變 class Charger<in T> { fun charging(device: T) {} } //聲明處協(xié)變 // 修改處 // ↓ class Shop<out T> { fun buy(): T { TODO("Not yet implemented") } }
對(duì)比可以發(fā)現(xiàn)逆變主要用于傳參,協(xié)變主要用于返回值,Kotlin的官方文檔也有這么一句話: ****消費(fèi)者 in, 生產(chǎn)者 out!
5.泛型邊界
在講協(xié)變的例子時(shí)我們要去商場(chǎng)Shop
買手機(jī),但是商場(chǎng)什么手機(jī)都賣,現(xiàn)在我想買Android手機(jī),怎么保證我買的就是Android手機(jī)?加上一個(gè)邊界就好了
// 變化在這里,加了一個(gè)邊界,類似于var x: Int = 0 // ↓ class Shop<out T: Android> { fun buy(): T { TODO("Not yet implemented") } } fun main() { val android = Shop<Android>() buy(android) val ios = Shop<IOS>() //報(bào)錯(cuò):類型不匹配 buy(ios) } }
6.星投影
星投影就是用【】作為泛型的實(shí)參,當(dāng)我們使用【】作為泛型的實(shí)參時(shí)也就意味著我們對(duì)具體的參數(shù)是什么并不感興趣或者說不知道具體的參數(shù)是什么。
舉例:還是買手機(jī)的案例,現(xiàn)在我不挑品牌了,只要能用就好,既然這樣那就隨便找家店鋪好了
// 不指定具體參數(shù) // ↓ fun findShop(): Shop<*> { TODO("Not yet implemented") } fun main(){ val shop = findShop() val product: Any? = shop.buy() }
這里的product
什么手機(jī)都可以,甚至是其他物品都行,這里還定義了一個(gè)Any?
也說明了可能是空手而歸。
那么我只想買個(gè)手機(jī),怎么才能避免買錯(cuò)成其他物品呢?添加邊界
//只找Phone的店鋪 // ↓ 這是邊界 class Shop<out T: Phone> { fun buy(): T { TODO("Not yet implemented") } } fun findShop(): Shop<*> { TODO("Not yet implemented") } fun main() { val shop = findShop() //只要返回值是Phone的商品 val product: Phone = shop.buy() }
添加邊界后就可以達(dá)到我只想買個(gè)手機(jī)的要求了。
泛型這一塊比較抽象,一定要多看幾遍,思考在項(xiàng)目中這個(gè)東西的應(yīng)用場(chǎng)景在哪里。
原文鏈接:https://juejin.cn/post/7170496238737948709
相關(guān)推薦
- 2022-09-29 LyScript實(shí)現(xiàn)內(nèi)存交換與差異對(duì)比的方法詳解_python
- 2023-05-05 Linux中g(shù)rep命令詳解_linux shell
- 2022-08-06 python實(shí)現(xiàn)去除空格及tab換行符的方法_python
- 2023-07-28 el-table 鼠標(biāo)懸浮時(shí)背景色改變
- 2022-07-13 kafka中Topic、消費(fèi)組以及消息狀態(tài)詳解
- 2022-07-04 PyG搭建GCN模型實(shí)現(xiàn)節(jié)點(diǎn)分類GCNConv參數(shù)詳解_python
- 2022-04-25 ASP.NET?Core?MVC中Tag?Helpers用法介紹_實(shí)用技巧
- 2022-09-06 Python的functools模塊使用及說明_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支