第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Kotlin 教程 Kotlin 教程 Kotlin 中的 typealias(別名)

Kotlin 中的 typealias 別名

今天一起來研究一下 Kotlin 中一個(gè)特殊的語法 typealias (別名),關(guān)于 typealias 類型別名,可能有的 Kotlin 開發(fā)人員接觸到過,有的還沒有碰到過。接觸過的,可能也用得不多,不知道如何更好地使用它。本篇文章會(huì)闡述了什么是類型別名、類型別名的使用場景、類型別名的實(shí)質(zhì)原理、類型別名和 import as 對比以及類型別名中需要注意的坑??赐赀@篇文章,仿佛打開 kotlin 中的又一個(gè)新世界,你將會(huì)很神奇發(fā)現(xiàn)一個(gè)小小 typealias 卻如此強(qiáng)大,深入實(shí)質(zhì)原理你又會(huì)發(fā)現(xiàn)原來也挺簡單的,但是無不被 kotlin 這門語言設(shè)計(jì)思想所折服,使用它可以大大簡化代碼以及提升代碼的可讀性。那么對于 Kotlin 的初學(xué)者以及正在使用 kotlin 開發(fā)的你來說,它可能會(huì)對你很有幫助。

1. 為什么需要 typealias

我們在寫 Kotlin 可能會(huì)寫很多 lambda 表達(dá)式 (閉包),并把它作為函數(shù)參數(shù)傳遞,可能閉包類型基本都一樣。比如下面這段代碼:

interface RestaurantPatron {
    fun makeReservation(restaurant: Organization<(Currency, Coupon?) -> Sustenance>)
    fun visit(restaurant: Organization<(Currency, Coupon?) -> Sustenance>)
    fun complainAbout(restaurant: Organization<(Currency, Coupon?) -> Sustenance>)
}

可以看到 RestaurantPatron 接口中三個(gè)函數(shù)參數(shù)都是同一個(gè) lambda 表達(dá)式類型: Organization<(Currency, Coupon?) -> Sustenance> ,很多類型的代碼被擠在一起的時(shí)候,就很容易迷失在代碼的細(xì)節(jié)中,所以這樣聲明看起來不簡潔也不利于維護(hù),代碼可讀性下降。那么此時(shí)就需要 typealias 改變這一切,使用 typealias 就可以很好地優(yōu)化上面的場景,代碼如下:

typealias Restaurant = Organization<(Currency, Coupon?) -> Sustenance> //typealias關(guān)鍵字聲明一個(gè)Restaurant別名
interface RestaurantPatron {
    fun makeReservation(restaurant: Restaurant)//在后面函數(shù)參數(shù)類型定義中就可以使用這個(gè)別名
    fun visit(restaurant: Restaurant)
    fun complainAbout(restaurant: Restaurant)
}   

優(yōu)化后的代碼看上去容易理解多,而且看到它時(shí),在代碼中的疑惑也會(huì)少很多。此外還能很好避免了在整個(gè) RestaurantPatron 接口中大量重復(fù)的類型,那么就不用每次去寫 Organization<(Currency, Coupon?) -> Sustenance> ,我們僅僅只有一種類型 Restaurant 即可。這樣也就意味著如果我們需要修改這種復(fù)雜類型也是很方便的。例如,如果我們需要將原來的 Organization<(Currency, Coupon?) -> Sustenance> 修改為 Organization<(Currency, Coupon?) -> Meal> ,我們僅僅只需要改變一處即可,而不是像原來那樣定義需要修改三個(gè)地方。

2. 什么是 typealias

我們已經(jīng)了解過如何簡單地去聲明一個(gè)類型 typealias 以及為什么需要 typealias,那么接下來會(huì)一起研究 typealias 原理是什么。
當(dāng)處理類型別名的時(shí)候,我們有兩個(gè)類型需要去思考:

  • 別名 (alias)
  • 底層類型 (underlying type)

圖片描述

可以把上述例子理解為本身是一個(gè)別名 (如 UserId), 或者包含別名 (如 List) 的縮寫類型,當(dāng) Kotlin 編譯器編譯代碼時(shí),所有使用到的相應(yīng)縮寫類型將會(huì)擴(kuò)展成原來的全類型。一起看個(gè)完整的例子

class UniqueIdentifier(val value: Int)

typealias UserId = UniqueIdentifier

val firstUserId: UserId = UserId(0)

當(dāng)編譯器處理上述代碼時(shí),所有對 UserId 的引用都會(huì)擴(kuò)展成 UniqueIdentifier,換句話說,在編譯期間,編譯器大部分是做了類似于在代碼中搜索別名 (UserId) 所有用到的地方,然后將代碼中用到的地方逐字地將其別名替換成全稱類型名 (UniqueIdentifier) 的工作
圖片描述

可能已經(jīng)注意到我說的是大部分場景。 這是因?yàn)槌酥鹱痔鎿Q原理,有一些特殊情況下 Kotlin 不完全是通過逐字替換原理來實(shí)現(xiàn)。但是大部分場景下,我們只需記住 typealias 是基于逐字替換原理即可。順便說一下,IntelliJ IDEA 和 AndroidStudio 已經(jīng)很智能了,它門會(huì)對類型別名有一些很好的支持。例如,可以在代碼中看到別名和底層類型:

圖片描述

并且能夠給出很好文檔和代碼提示

圖片描述

總結(jié)下,實(shí)際上 typealias 別名并不會(huì)去真的創(chuàng)建一個(gè)新的類型,而僅僅是取了一個(gè)別名,然后在編譯器編譯期間,把別名又類似逐字替換原理換回實(shí)際的類型,這樣就能保證編譯正常也不會(huì)產(chǎn)生新的類型。

3. 如何使用 typealias

使用 typealias 別名非常簡單,只需要使用 typealias 關(guān)鍵字在代碼頂層聲明即可,然后只要在后面需要用到對應(yīng)別名聲明對應(yīng)的類型即可。這里用 Kotlin 集合中的 ArrayList 源碼舉例,它實(shí)際上就是一個(gè) java.lang.ArrayList 的一個(gè)別名。

@SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public actual typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public actual typealias HashSet<E> = java.util.HashSet<E>

4. typealias 的使用場景

4.1 typealias 用于多數(shù)通用場景

// Classes and Interfaces (類和接口)
typealias RegularExpression = String
typealias IntentData = Parcelable

// Nullable types (可空類型)
typealias MaybeString = String?

// Generics with Type Parameters (類型參數(shù)泛型)
typealias MultivaluedMap<K, V> = HashMap<K, List<V>>
typealias Lookup<T> = HashMap<T, T>

// Generics with Concrete Type Arguments (混合類型參數(shù)泛型)
typealias Users = ArrayList<User>

// Type Projections (類型投影)
typealias Strings = Array<out String>
typealias OutArray<T> = Array<out T>
typealias AnyIterable = Iterable<*>

// Objects (including Companion Objects) (對象,包括伴生對象)
typealias RegexUtil = Regex.Companion

// Function Types (函數(shù)類型)
typealias ClickHandler = (View) -> Unit

// Lambda with Receiver (帶接收者的Lambda)
typealias IntentInitializer = Intent.() -> Unit

// Nested Classes and Interfaces (嵌套類和接口)
typealias NotificationBuilder = NotificationCompat.Builder
typealias OnPermissionResult = ActivityCompat.OnRequestPermissionsResultCallback

// Enums (枚舉類)
typealias Direction = kotlin.io.FileWalkDirection
// (but you cannot alias a single enum *entry*)

// Annotation (注解)
typealias Multifile = JvmMultifileClass

4.2 typealias 用于構(gòu)造器函數(shù)特殊場景

如果底層類型有一個(gè)構(gòu)造器,那么它的類型別名也可以使用。甚至可以在一個(gè)可空類型的別名上調(diào)用構(gòu)造函數(shù)!

class TeamMember(val name: String)
typealias MaybeTeamMember = TeamMember?

//使用別名來構(gòu)造對象
val member =  MaybeTeamMember("Miguel")

// 以上代碼不會(huì)是逐字?jǐn)U展成如下無法編譯的代碼
val member = TeamMember?("Miguel")

// 而是轉(zhuǎn)換成如下代碼
val member = TeamMember("Miguel")

所以可以看到編譯時(shí)的擴(kuò)展并不總是逐字?jǐn)U展的,在這個(gè)例子中就是很有效的說明。
如果底層類型本身就沒有構(gòu)造器 (例如接口或者類型投影),自然地你也不可能通過別名來調(diào)用構(gòu)造器。

4.3 typealias 用于伴生對象 compaion object

可以通過含有伴生對象類的別名來調(diào)用該類的伴生對象中的屬性和方法。即使底層類型具有指定的具體類型參數(shù),也是如此。一起看下如下代碼:

class Container<T>(var item: T) {
    companion object {
        const val classVersion = 5
    }
}

// 注意此處的String是具體的參數(shù)類型
typealias BoxedString = Container<String>

// 通過別名獲取伴侶對象的屬性
val version = BoxedString.classVersion

// 這行代碼不會(huì)是擴(kuò)展成如下無法編譯的代碼
val version = Container<String>.classVersion

// 它是會(huì)在即將進(jìn)入編譯期會(huì)擴(kuò)展成如下代碼
val version = Container.classVersio

5. typealias 與 import as 的區(qū)別

其實(shí)在 Kotlin 中還有一個(gè)非常類似于類型別名 (type lias) 的概念,叫做 Import As. 它允許你給一個(gè)類型、函數(shù)或者屬性一個(gè)新的命名,然后你可以把它導(dǎo)入到一個(gè)文件中。例如:

import android.support.v4.app.NotificationCompat.Builder as NotificationBuilder

在這種情況下,我們從 NotificationCompat 導(dǎo)入了 Builder 類,但是在當(dāng)前文件中,它將以名稱 NotificationBuilder 的形式出現(xiàn)。
你是否遇到過需要導(dǎo)入兩個(gè)同名的類的情況?
如果有,那么你可以想象一下 Import As 將會(huì)帶來巨大的幫助,因?yàn)樗馕吨悴恍枰ハ薅ㄟ@些類中某個(gè)類。
例如,查看以下 Java 代碼,我們可以將數(shù)據(jù)庫模型中的 User 轉(zhuǎn)換為 service 模型的 User。

package com.example.app.service;

import com.example.app.model.User;

public class UserService {
    public User translateUser(com.example.app.database.User user) {
        return new User(user.getFirst() + " " + user.getLast());
    }
}

由于此代碼處理兩個(gè)不同的類,但是這兩個(gè)類都叫 User,因此我們無法將它們兩者都同時(shí)導(dǎo)入。相反,我們只能將其中某個(gè)以類名 + 包名全稱使用 User。
利用 Kotlin 中的 Import As, 就不需要以全稱類名的形式使用,僅僅只需要給它另一個(gè)命名,然后去導(dǎo)入它即可。

package com.example.app.service

import com.example.app.model.User
import com.example.app.database.User as DatabaseUser

class UserService {
    fun translateUser(user: DatabaseUser): User =
            User("${user.first} ${user.last}")
}

此時(shí)的你,或許想知道,類型別名 (type alias) 和 Import As 之間的區(qū)別?畢竟,您還可以用 typealias 消除 User 引用的沖突,如下所示:

package com.example.app.service
import com.example.app.model.User
typealias DatabaseUser = com.example.app.database.User
class UserService {
    fun translateUser(user: DatabaseUser): User =
            User("${user.first} ${user.last}")
}

沒錯(cuò),事實(shí)上,除了元數(shù)據(jù) (metadata) 之外,這兩個(gè)版本的 UserService 都可以編譯成相同的字節(jié)碼!
所以,問題來了,你怎么去選擇你需要那一個(gè)?它們之間有什么不同?這里列舉了一系列有關(guān) typealiasimport as 各自支持特性情況如下:

目標(biāo)對象 Typealias 別名 import as
Interfaces and Classes (接口和類) YES NO
Nullable Types (可空類型) YES NO
Generics with Type Params (泛型類型參數(shù)) YES NO
Function Types (函數(shù)類型) YES NO
Enum (枚舉類型) YES YES
Enum Member (枚舉成員) NO YES
object (對象表達(dá)式) YES YES
object Function (對象表達(dá)式函數(shù)) NO YES
object Properties (對象表達(dá)式屬性) NO YES

此外還需要注意的是:

  • 類型別名可以具有可見性修飾符,如 internalprivate ,而它訪問的范圍是整個(gè)文件;
  • 如果您從已經(jīng)自動(dòng)導(dǎo)入的包中導(dǎo)入類,例如 kotlin.*kotlin.collections* ,那么您必須通過該名稱引用它。 例如,如果您要將 import kotlin.String 寫為 RegularExpression ,則 String 的用法將引用 java.lang.String .

6. 總結(jié)

到這里,有關(guān) Kotlin 中的 typealias 的別名就闡述完畢了。相信你對 typealias 的認(rèn)識(shí)更深了,并且知道它和 import as 之間區(qū)別以及分別使用場景。下面有幾點(diǎn)結(jié)論總結(jié)需要理解和記憶:

  • 類型別名 (typealias) 不會(huì)創(chuàng)建新的類型,只是給現(xiàn)有類型取了另一個(gè)名稱而已;
  • typealias 實(shí)質(zhì)原理,大部分情況下是在編譯時(shí)期采用了逐字替換的擴(kuò)展方式,還原成真正的底層類型;但是不是完全是這樣的,正如本文例子提到的那樣;
  • typealias 只能定義在頂層位置,不能被內(nèi)嵌在類、接口、函數(shù)等內(nèi)部。