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
equals
与 hashCode
ID 下所有类型均允许互相通过 ID.equals 判断是否具有相同的 字面值。 ID.equals 实际上不会判断类型,因此如果两个不同类型的 ID 的字面值相同, 例如值为 "1"
的 StringID 和值为 1
的 IntID,它们之间使用 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 本身, 但是不同类型但字面值相同的 ID 的 hashCode 值可能并不相同, 因此 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
Inherited properties
Inherited functions
尝试将 this 转化为 Int。 如果为 NumericalID 则直接使用 NumericalID.toInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toInt()。
尝试将 this 转为 IntID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toInt。
尝试将 this 转化为 Int。 如果为 NumericalID 则直接使用 NumericalID.toInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toIntOrNull()。
尝试将 this 转化为 Long。 如果为 NumericalID 则直接使用 NumericalID.toLong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toLong()。
尝试将 this 转为 LongID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toLong。
尝试将 this 转化为 Long。 如果为 NumericalID 则直接使用 NumericalID.toLong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toLongOrNull()。
尝试将 this 转化为 UInt。 如果为 NumericalID 则直接使用 NumericalID.toInt.toUInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toUInt()。
尝试将 this 转为 UIntID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toUInt。
尝试将 this 转化为 UInt。 如果为 NumericalID 则直接使用 NumericalID.toInt.toUInt, 否则使用 notNumerical 转化。默认会尝试使用 literal.toUIntOrNull()。
尝试将 this 转化为 ULong。 如果为 NumericalID 则直接使用 NumericalID.toLong.toULong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toULong()。
尝试将 this 转为 ULongID 类型。 如果不是数字ID,则会使用 notNumerical 获取结果。默认使用 String.toULong。
尝试将 this 转化为 ULong。 如果为 NumericalID 则直接使用 NumericalID.toLong.toULong, 否则使用 notNumerical 转化。默认会尝试使用 literal.toULongOrNull()。