ID

ID 是用于表示 唯一标识 的不可变类值类型。

ID 是一个密封类型,它提供的最终类型有:

它们可以粗略的被归类为字符串类型( UUID 的字面值表现为字符串)和数字类型。

构造

在 Kotlin 中,直接使用各类型的伴生对象提供的扩展属性构建即可:

100.ID
100L.ID
100u.ID
"100".ID
UUID.random()

在 Java 中,可以使用 Identifies 中提供的构造方法,它们通常被命名为 of 或以 of 为开头:

Identifies.of(100);
Identifies.of(100L);
Identifies.of("100");
Identifies.ofULong(100L);
Identifies.ofULong("100");
Identifies.uuid();

也可以使用具体类型的伴生对象所提供的静态API:

IntID.valueOf(100);
LongID.valueOf(100L);
UIntID.valueOf(100);
ULongID.valueOf(100L);
StringID.valueOf("100");
UUID.random();

这些伴生对象提供的静态API与 Identifies 中的内容相比缺少了一些辅助性的API, 例如使用字符串构建无符号ID ULongID

序列化

所有具体的ID类型都是可序列化的,它们都会通过 Kotlinx serialization 提供一个可作为字面值序列化的序列化器实现。

所有的ID类型都不会被序列化为结构体,例如 UIntID 会被直接序列化为一个数字:

@Serializable
data class Foo(val value: UIntID)
// 序列化结果: {"value": 123456}

顺序

ID 实现 Comparable 并允许所有 ID 类型之间存在排序关系。 具体的排序规则参考每个具体的 ID 类型的 compareTo 的文档说明。

字面值与 toString

一个 ID 所表示的字符串值即为其字面值,也就是 ID.toString 的输出结果。

对于 StringID 来说,字面值就是它内部的字符串的值。

val id = "abc".ID
// toString: abc

对于 UUID 来说,字面值即为其内部 128 位数字通过一定算法计算而得到的具有规律且唯一的字符串值。

val id = UUID.random()
// toString: 817d2625-1c9b-4cc4-880e-5d6ba86a42b7

对于各数字类型的ID NumericalID 来说,字面值即为数字转为字符串的值。

val iID: IntID = 1.ID     // toString: 1
val lID: LongID = 1L.ID // toString: 1
val uiID: UIntID = 1u.ID // toString: 1
val ul: ULong = 1u
val ulID: ULongID = ul.ID // toString: 1

在Java中需要尤其注意的是,一个相同的数值, 使用无符号类型和有符号类型的ID在通过 valueOf 构建的结果可能是不同的,获取到的 value 和字面值也可能是不同的。

Java在操作无符号ID的时候需要注意使用相关的无符号API。 以 long 为例:

long value = -1;

LongID longID = LongID.valueOf(value);
ULongID uLongID = ULongID.valueOf(value);

System.out.println(longID); // -1
System.out.println(uLongID); // 18446744073709551615

System.out.println(longID.getValue()); // -1
System.out.println(uLongID.getValue()); // -1

如果希望得到一些符合预期的结果,你应该使用Java中的无符号相关API:

long value = Long.parseUnsignedLong("18446744073709551615");
ULongID uLongID = ULongID.valueOf(value);
System.out.println(uLongID); // 18446744073709551615
System.out.println(Long.toUnsignedString(uLongID.getValue()));
// 18446744073709551615

equalshashCode

ID 下所有类型均允许互相通过 ID.equals 判断是否具有相同的 字面值ID.equals 实际上不会判断类型,因此如果两个不同类型的 ID 的字面值相同, 例如值为 "1"StringID 和值为 1IntID,它们之间使用 ID.equals 会得到 true

ID 作为一个"唯一标识"载体,大多数情况下,它的类型无关紧要。 并且 ID 属性的提供者也应当以抽象类 ID 类型本身对外提供, 而将具体的类型与构建隐藏在实现内部。

public interface Foo {
val id: ID
}

internal class FooImpl(override val id: ULongID) : Foo

如果你希望严格匹配两个 ID 类型,而不是将它们视为统一的"唯一标识",那么使用 ID.equalsExact 来进行。equalsExact 会像传统数据类型的 equals 一样,同时判断类型与值。

由于 ID 类型之间通过 equals 会仅比较字面值,且对外应仅暴露 ID 本身, 但是不同类型但字面值相同的 IDhashCode 值可能并不相同, 因此 ID 这个抽象类型本身 不适合 作为一种 hash Key, 例如作为 HashMap 的 Key:

// ❌Bad!
val map1 = hashMapOf<ID, String>("1".ID to "value 1", 1.ID to "also value 1")
// size: 2, values: {1=value 1, 1=also value 1}
// ❌Bad!
val uuid = UUID.random()
val strId = uuid.toString().ID
val map2 = hashMapOf<ID, String>(uuid to "UUID value", strId to "string ID value")
// size: 2, values: {2afb3d3e-d3f4-4c15-89ed-eec0e258d533=UUID value, 2afb3d3e-d3f4-4c15-89ed-eec0e258d533=string ID value}

如果有必要,你应该使用一个具体的最终ID类型作为某种 hash key, 例如:

// ✔ OK.
val map = hashMapOf<IntID, String>(1.ID to "value 1", 1.ID to "also value 1")
// size: 1, values: {1=also value 1}

Author

ForteScarlet

Inheritors

Constructors

Link copied to clipboard
protected constructor()

Inherited properties

Link copied to clipboard

ID.toString, 得到 ID 的字面值。

Functions

Link copied to clipboard
abstract fun copy(): ID

复制一个当前ID。

Link copied to clipboard
abstract operator override fun equals(other: Any?): Boolean

判断另外一个 ID 是否与当前 ID 字面值相同

Link copied to clipboard
abstract fun equalsExact(other: Any?): Boolean

判断另外一个 ID 是否与当前 ID 字面值与类型均相同

Link copied to clipboard
abstract override fun hashCode(): Int

ID 的源值 hashcode,等于对应的源值的 hashcode。

Link copied to clipboard
abstract override fun toString(): String

ID 的 字面值 字符串。

Inherited functions

Link copied to clipboard
abstract operator fun compareTo(other: ID): Int
Link copied to clipboard
@JvmName(name = "toInt")
inline fun ID.toInt(notNumerical: ID.() -> Int = { literal.toInt() }): Int

尝试将 this 转化为 Int。 如果为 NumericalID 则直接使用 NumericalID.toInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toInt()

Link copied to clipboard
@JvmName(name = "toIntID")
inline fun ID.toIntID(notNumerical: ID.() -> IntID = { literal.toInt().ID }): IntID

尝试将 this 转为 IntID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toInt

Link copied to clipboard
@JvmName(name = "toIntOrNull")
inline fun ID.toIntOrNull(notNumerical: ID.() -> Int? = { literal.toIntOrNull() }): Int?

尝试将 this 转化为 Int。 如果为 NumericalID 则直接使用 NumericalID.toInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toIntOrNull()

Link copied to clipboard
@JvmName(name = "toLong")
inline fun ID.toLong(notNumerical: ID.() -> Long = { literal.toLong() }): Long

尝试将 this 转化为 Long。 如果为 NumericalID 则直接使用 NumericalID.toLong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toLong()

Link copied to clipboard
@JvmName(name = "toLongID")
inline fun ID.toLongID(notNumerical: ID.() -> LongID = { literal.toLong().ID }): LongID

尝试将 this 转为 LongID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toLong

Link copied to clipboard
@JvmName(name = "toLongOrNull")
inline fun ID.toLongOrNull(notNumerical: ID.() -> Long? = { literal.toLongOrNull() }): Long?

尝试将 this 转化为 Long。 如果为 NumericalID 则直接使用 NumericalID.toLong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toLongOrNull()

Link copied to clipboard
@JvmName(name = "toUInt")
inline fun ID.toUInt(notNumerical: ID.() -> UInt = { literal.toUInt() }): UInt

尝试将 this 转化为 UInt。 如果为 NumericalID 则直接使用 NumericalID.toInt.toUInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toUInt()

Link copied to clipboard
@JvmName(name = "toUIntID")
inline fun ID.toUIntID(notNumerical: ID.() -> UIntID = { literal.toUInt().ID }): UIntID

尝试将 this 转为 UIntID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toUInt

Link copied to clipboard
@JvmName(name = "toUIntOrNull")
inline fun ID.toUIntOrNull(notNumerical: ID.() -> UInt? = { literal.toUIntOrNull() }): UInt?

尝试将 this 转化为 UInt。 如果为 NumericalID 则直接使用 NumericalID.toInt.toUInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toUIntOrNull()

Link copied to clipboard
@JvmName(name = "toULong")
inline fun ID.toULong(notNumerical: ID.() -> ULong = { literal.toULong() }): ULong

尝试将 this 转化为 ULong。 如果为 NumericalID 则直接使用 NumericalID.toLong.toULong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toULong()

Link copied to clipboard
@JvmName(name = "toULongID")
inline fun ID.toULongID(notNumerical: ID.() -> ULongID = { literal.toULong().ID }): ULongID

尝试将 this 转为 ULongID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toULong

Link copied to clipboard
@JvmName(name = "toULongOrNull")
inline fun ID.toULongOrNull(notNumerical: ID.() -> ULong? = { literal.toULongOrNull() }): ULong?

尝试将 this 转化为 ULong。 如果为 NumericalID 则直接使用 NumericalID.toLong.toULong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toULongOrNull()