Я играю с идеей использования встроенных классов 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)
}
Идея здесь состоит в том, чтобы определить методы для операций, которые:
- Оставляют единицы без изменений (например:сумма, умножение на чистое значение)
- Результат на чистое значение (например, деление на значение той же единицы)
- Результат на значение другой определенной единицы (например:
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)
Но, конечно, это не сработает, потому что я не могу создать экземпляр такого обобщенного типа.Есть ли способ добиться этого?