5.4.9 类型别名

原文链接: https://kotlinlang.org/docs/type-aliases.html

5.4.9 类型别名

类型别名为已有类型提供替代名称。它可以让冗长或频繁使用的类型表达式变得更短、更易于理解。

例如,你可以为泛型类型、函数类型以及嵌套类或内部类创建别名:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 泛型类型
typealias UserIndex = Map<Long, User>
typealias FileTable<K> = MutableMap<K, MutableList<File>>

// 函数类型
typealias RequestHandler = (Request) -> Response
typealias Predicate<T> = (T) -> Boolean

// 内部类与嵌套类
class Database {
    inner class Transaction
}
typealias DatabaseTransaction = Database.Transaction

类型别名并不会创建新类型,它只是为已有类型引入一个替代名称。别名与其底层类型可以互换使用。例如,当你声明 typealias Predicate<T> 并使用 Predicate<Int> 时,编译器会把它展开为 (Int) -> Boolean。在用到底层类型的地方,你都可以使用以别名声明的值,反之亦然:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
typealias Predicate<T> = (T) -> Boolean

fun evaluate(predicate: Predicate<Int>) = predicate(42)

fun main() {
    val isPositive: (Int) -> Boolean = { it > 0 }
    println(evaluate(isPositive))
    // true

    val isValid: Predicate<Int> = { it > 0 }
    println(listOf(1, -2).filter(isValid))
    // [1]
}

声明类型别名

你可以在以下位置声明类型别名:

你不能在局部作用域中声明类型别名,例如函数或 lambda 表达式 内部。

声明位置决定了类型别名的作用域,而它的可见性决定了哪些代码可以访问它。默认情况下,类型别名是 public 的。嵌套类型别名只能在其所在的类、接口或对象可访问的地方访问。例如,internal 类中的 public 别名无法从模块外部访问。

类型别名不能暴露可见性比自身更严格的底层类型。例如,public 类型别名不能引用 private 类。

顶层类型别名

顶层类型别名是包级声明。在同一包内,你可以用其非限定名引用别名。若要在其他包中使用该别名,请导入它,或用其限定名引用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// UserId.kt
package org.example.users

typealias UserId = Long

// 在同一包内用非限定名引用该别名
fun createUser(id: UserId) {
    // ...
}

// UserService.kt
package org.example.services

import org.example.users.UserId

// 使用导入的别名,以其非限定名引用
fun findUser(id: UserId) {
    // ...
}

// 使用完全限定名
fun deleteUser(id: org.example.users.UserId) {
    // ...
}

嵌套类型别名

嵌套类型别名可以提升封装性、减少包级命名杂乱并简化内部实现,从而让代码更清晰、更易维护。嵌套类型别名遵循与嵌套类相同的作用域和名称解析规则。

当替代名称只在该声明的上下文中才有意义时,就在类、接口或对象内部声明类型别名。这样可以让别名与使用它的代码更接近,也避免向包作用域再添加一个名称。

在包含它的声明内部,你可以用其非限定名引用别名。在该声明之外,需要用包含它的声明名对别名进行限定:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class UserRepository {
    typealias UserIndex = Map<UserId, User>

    // 在 UserRepository 内部用其非限定名引用该别名
    fun saveAll(users: UserIndex) {
        // ...
    }
}

// 在 UserRepository 外部用其限定名引用该别名
fun synchronizeUsers(users: UserRepository.UserIndex) {
    // ...
}

注意: Kotlin Multiplatform 的 expect/actual 声明不支持嵌套类型别名。

类型参数

要在嵌套类型别名中使用类型参数,请在别名声明中添加它们:

1
2
3
4
5
class Graph<Node> {
    typealias Path<T> = List<T>
}

val cityPath: Graph.Path<String> = listOf("London", "Berlin")

在这个例子中,Path 声明了自己的类型参数 T。在 Graph.Path<String> 中,String 是 T 的类型实参,它与 Graph 声明的 Node 类型参数彼此独立。

如果你引用的是由包含它的类或接口声明的类型参数,编译器会报错:

1
2
3
4
class Graph<Node> {
    typealias Path = List<Node>
    // 未解析的引用 'Node'。
}

这里 Path 引用的是来自 Graph 的 Node,而不是声明自己的类型参数。