Общие методы и операторы для встроенных классов, используемых в качестве единиц измерения в Котлине - PullRequest
0 голосов
/ 07 декабря 2018

Я играю с идеей использования встроенных классов Kotlin для выражения безопасных типов операций с единицами измерения.Например, давайте определим единицы измерения для расстояния, времени и скорости:

inline class Meters(val v: Float) {
    operator fun plus(other: Meters) = Meters(v + other.v)
    operator fun times(amount: Float) = Meters(v * amount)
    operator fun compareTo(other: Meters) = v.compareTo(other.v)
    operator fun div(other: Meters): Float = v / other.v

    fun calcSpeed(time: Seconds) = MetersPerSecond(v * time.v)
    // operator fun times(time: Seconds) = MetersPerSecond(v / time.v) // clash (for now?)
}

inline class Seconds(val v: Float) {
    operator fun plus(other: Seconds) = Seconds(v + other.v)
    operator fun times(amount: Float) = Seconds(v * amount)
    operator fun compareTo(other: Seconds) = v.compareTo(other.v)
    operator fun div(other: Seconds): Float = v / other.v

    fun calcSpeed(distance: Meters) = MetersPerSecond(distance.v / v)
}

inline class MetersPerSecond(val v: Float) {
    operator fun plus(other: MetersPerSecond) = MetersPerSecond(v + other.v)
    operator fun times(amount: Float) = MetersPerSecond(v * amount)
    operator fun compareTo(other: MetersPerSecond) = v.compareTo(other.v)

    fun calcDistance(time: Seconds) = Meters(v * time.v)
    fun calcTime(distance: Meters) = Seconds(distance.v / v)
}

Идея здесь состоит в том, чтобы определить методы для операций, которые:

  1. Оставляют единицы без изменений (например:сумма, умножение на чистое значение)
  2. Результат на чистое значение (например, деление на значение той же единицы)
  3. Результат на значение другой определенной единицы (например: time * distance = speed)

Чтобы можно было написать такие выражения, как:

val distance = Meters(1f)
val time = Seconds(1f)
val speed: MetersPerSecond = (distance * 0.5f).calcSpeed(time)

Глядя на операторы в примере, относящемся к случаям 1 и 2, я ясно вижу образец иМне интересно, есть ли способ определить эти методы один раз в более общем виде для всех типов, которые нуждаются в этом поведении «единица измерения».

Я думал о том, чтобы иметь общий интерфейс и определять операторы как функции расширения с обобщениями:

interface UnitOfMeasurement { val v: Float }
operator fun <T: UnitOfMeasurement> T.plus(other: T) = T(v + other.v)

Но, конечно, это не сработает, потому что я не могу создать экземпляр такого обобщенного типа.Есть ли способ добиться этого?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...