9.1.6 UUID

原文链接: https://kotlinlang.org/docs/uuids.html

9.1.6 UUID

Uuid 类表示通用唯一标识符(UUID),也称为全局唯一标识符(GUID)。

Uuid 是一个 128 位的值,用于唯一标识某个实体,而无需依赖集中分配 ID 的系统。这让 UUID 在分布式应用、数据库、客户端生成的记录或 Kotlin Multiplatform 应用中非常有用。

使用 Uuid 类来处理 UUID 值。与普通字符串不同,专用的 UUID 类型让代码更明确,并可防止误用无效的值。

要在项目中使用 UUID,请从 kotlin.uuid 包中导入 Uuid 类:

1
import kotlin.uuid.Uuid

生成 UUID

要为常规标识符(例如用户 ID 或数据库 ID)生成随机的版本 4 UUID,请使用 Uuid.random() 函数:

1
2
3
4
5
6
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.random()
    println(id)
}

你还可以用以下实验性函数生成特定版本的 UUID:

这些 UUID 生成函数是实验性的。要选择启用,请使用 @OptIn(ExperimentalUuidApi::class) 注解,或在构建文件中添加以下编译器选项:

Gradle

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-opt-in=kotlin.uuid.ExperimentalUuidApi")
    }
}

Maven

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<build>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <configuration>
                <args>
                    <arg>-opt-in=kotlin.uuid.ExperimentalUuidApi</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

下面是一个生成特定版本 UUID 的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import kotlin.time.Instant
import kotlin.time.ExperimentalTime
import kotlin.uuid.Uuid

@OptIn(kotlin.uuid.ExperimentalUuidApi::class, ExperimentalTime::class)
fun main() {
    // 生成一个版本 4 UUID
    val idVersion4 = Uuid.generateV4()
    println(idVersion4)

    // 生成一个版本 7 UUID
    val idVersion7 = Uuid.generateV7()
    println(idVersion7)

    // 为指定的时间戳生成一个版本 7 UUID
    val timestamp = Instant.fromEpochMilliseconds(1757440583000L)
    val idVersion7SpecificTime = Uuid.generateV7NonMonotonicAt(timestamp)
    println(idVersion7SpecificTime)
}

解析 UUID

UUID 值通常表示为字符串,例如在 URL 参数或数据库记录中。

要把 String 值转换为 Uuid 值,请使用 Uuid.parse() 函数:

1
2
3
4
5
6
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.parse("de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a")
    println(id)
}

Uuid.parse() 函数既接受标准的十六进制加连字符格式,也接受不带连字符的十六进制格式。

如果输入无效,Uuid.parse() 函数会抛出 IllegalArgumentException:

1
2
3
4
5
6
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.parse("10")
    println(id)
}

如果你的应用只接受一种表示形式,请使用特定格式的函数:

例如:

1
2
3
4
5
6
7
8
9
import kotlin.uuid.Uuid

fun main() {
    val standard = Uuid.parseHexDash("de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a")
    val compact = Uuid.parseHex("de2bc56cea734f3c8a375a46fdb2d79a")

    println(standard)
    println(compact)
}

如果你有来自外部的 UUID,并且必须安全地处理无效输入,请使用 Uuid.parseOrNull()、Uuid.parseHexDashOrNull() 或 Uuid.parseHexOrNull()。这些函数在输入无效时返回 null:

1
2
3
fun parseId(input: String): Uuid? {
    return Uuid.parseOrNull(input)
}

把 UUID 转换为字符串

你可以用以下函数把 Uuid 值转换为 String 值:

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.parse("de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a")

    println(id.toString())
    // de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a
    println(id.toHexDashString())
    // de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a
    println(id.toHexString())
    // de2bc56cea734f3c8a375a46fdb2d79a
}

比较 UUID

你可以用 == 运算符检查 Uuid 值是否相等。

Kotlin 根据 UUID 的值来比较,而不是根据其文本表示。例如,只要两种不同形式表示的是同一个 128 位值,它们就相等:

1
2
3
4
5
6
7
8
9
import kotlin.uuid.Uuid

fun main() {
    val first = Uuid.parse("de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a")
    val second = Uuid.parse("de2bc56cea734f3c8a375a46fdb2d79a")

    println(first == second)
    // true
}

这使 Uuid 比较比字符串比较更可靠,因为字符串比较会把同一值的不同格式视为不同的值。Uuid 比较检查的是实际的标识符值。

Uuid 实现了 Comparable<Uuid> 接口,因此 UUID 值可以用 sorted() 等标准集合函数排序。在这种情况下,Kotlin 按字典序比较(从最高位到最低位):

1
2
3
4
5
6
7
8
9
import kotlin.uuid.Uuid

fun main() {
    val first = Uuid.generateV7()
    val second = Uuid.generateV7()

    val sorted = listOf(first, second).sorted()
    println(sorted)
}

处理二进制表示

某些 API、存储格式和二进制协议并不把 UUID 表示为字符串,而是把 128 位的 UUID 值存储为以下形式之一:

  • 16 字节数组
  • 两个 64 位值

当你需要与期望二进制 UUID 数据的系统交换 UUID 时,请使用这些表示形式。

要在 UUID 与 16 字节表示之间转换,请使用 .toByteArray() 和 Uuid.fromByteArray() 函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.random()

    val bytes = id.toByteArray()
    val original = Uuid.fromByteArray(bytes)

    println(id)

    println(bytes)
    println(original)

    println(id == original)
    // true
}

你也可以把同一个 128 位 UUID 值表示为两个 Long 值。这很有用,因为 Kotlin 没有内置的 128 位整数类型。这两个 Long 值分两部分存储 UUID:

  • mostSignificantBits 参数用于 UUID 的前 64 位。
  • leastSignificantBits 参数用于 UUID 的后 64 位。

要从两个 Long 值创建 Uuid 值,请使用 Uuid.fromLongs() 函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.fromLongs(
        mostSignificantBits = -4653685776373167443,
        leastSignificantBits = -6288180676521310383.toLong()
    )
    println(id)
    // bf6ac971-52fd-4aad-a8bb-e4fdac78c751
}

要从现有的 Uuid 值中提取这两部分,请使用 Uuid.toLongs() 函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import kotlin.uuid.Uuid

fun main() {
    val id = Uuid.random()

    id.toLongs { mostSignificantBits, leastSignificantBits ->
        println(mostSignificantBits)
        println(leastSignificantBits)
    }
}

序列化 UUID

Kotlin 支持对 Uuid 值进行序列化。你可以用它把 UUID 值存储或传输到 Kotlin 代码之外,例如在 JSON API 或配置文件中。

要序列化 Uuid 值,除非你的应用需要其他格式,否则请把它表示为字符串。kotlinx.serialization 库使用十六进制加连字符格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import kotlin.uuid.Uuid
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable
data class User(
    val id: Uuid,
    val name: String
)

fun main() {
    val user = User(
        id = Uuid.parse("de2bc56cea734f3c8a375a46fdb2d79a"),
        name = "Kotlin"
    )

    println(Json.encodeToString(user))
    // {"id":"de2bc56c-ea73-4f3c-8a37-5a46fdb2d79a","name":"Kotlin"}
}

与 Java API 一起使用 UUID

Java 使用 java.util.UUID 类表示 UUID。在 JVM 上,Java API 可能接受或返回这种类型。虽然 java.util.UUID 和 kotlin.uuid.Uuid 都表示 UUID,但它们是两种不同的类型。

在 Kotlin 与 Java 之间传递 UUID 时,请显式转换值:

1
2
3
  import kotlin.uuid.toKotlinUuid

  val kotlinId: Uuid = javaId.toKotlinUuid()
  • 用 .toJavaUuid() 扩展函数把 Kotlin UUID 转换为 Java 的:
1
2
3
  import kotlin.uuid.toJavaUuid

  val javaId: java.util.UUID = kotlinId.toJavaUuid()

这些函数让你可以在 JVM 互操作边界上使用 Uuid 来表示 UUID 值。

注意: java.util.UUID 和 kotlin.uuid.Uuid 类可以比较,但顺序可能不同。从 Java API 迁移到 Kotlin API 之前,请务必检查依赖 UUID 顺序的代码。

Kotlin 还提供了对 Java 缓冲区的支持。使用 JVM 特有的函数在 ByteBuffer 中处理 UUID:

  • 使用 .getUuid() 函数从缓冲区中读取 UUID。
  • 使用 .putUuid() 函数把 UUID 写入缓冲区。