自定义'typesafe'Int类型

avb*_*avb 1 kotlin

我想要的是两种不同的整数类型,它们在语义上是可区分的.

例如,在此代码中,"Meter"类型和"Pixel"int类型

typealias Meter = Int
typealias Pixel = Int

fun Meter.toPixel() = this * 100
fun Pixel.toMeter() = this / 100

fun calcSquareMeters(width: Meter, height: Meter) = width * height
fun calcSquarePixels(width: Pixel, height: Pixel) = width * height

fun main(args: Array<String>) {
    val pixelWidth: Pixel = 50
    val pixelHeight: Pixel = 50

    val meterWidth: Meter = 50
    val meterHeight: Meter = 50

    calcSquareMeters(pixelWidth, pixelHeight) // (a) this should not work

    pixelWidth.toPixel() // (b) this should not work
}
Run Code Online (Sandbox Code Playgroud)

这个解决方案的问题是

(a)我可以用我的'Pixel'类型调用calcSquareMeters,我不希望这样

(b)我可以调用toPixel()扩展函数,我只想在我的'Pixel'类型上使用我的'Meter'类型,我不希望这样.

我想这是typealias的预期行为,所以我想实现我的目标我必须使用与typealias不同的东西......

那我该怎么做呢?

The*_*tor 5

除了现有的答案:如果您在这两种类型之间有很多共同的功能并且不想复制它,您可以使用接口:

interface MetricType<T> {
    val value: Int

    fun new(value: Int): T
}

data class Meter(override val value: Int) : MetricType<Meter> {
    override fun new(value: Int) = Meter(value)
}

data class Pixel(override val value: Int) : MetricType<Pixel> {
    override fun new(value: Int) = Pixel(value)
}
Run Code Online (Sandbox Code Playgroud)

像这样,您可以轻松地在基本界面上定义操作,例如加法,减法和缩放:

operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)
Run Code Online (Sandbox Code Playgroud)

界面和泛型的组合可确保类型安全,因此您不会意外混合类型:

fun test() {
    val m = Meter(3)
    val p = Pixel(7)

    val mm = m + m // OK
    val pp = p + p // OK
    val mp = m + p // does not compile
}
Run Code Online (Sandbox Code Playgroud)

请记住,由于虚函数,此解决方案的运行时成本较高(与分别重写每种类型的运算符相比).这除了创建对象的开销之外.