Как хранить временную переменную при инициализации объекта Kotlin?

Я изучаю Котлин и, как часть обучения, хочу создать класс, который будет представлять собой рациональное число, требования:

  • Класс должен содержать два неизменяемых целочисленных поля: числитель и знаменатель.
  • Класс должен содержать допустимые значения equals, hashCode и toString.
  • Когда класс инициализируется, числитель и знаменатель должны быть удалены их GCD (это означает, что Ratio(1, 2) == Ratio(2, 4 /* or 4, 8 */) or Ratio(2, 4 /* or 4, 8 */).numerator == 1, .denominator == 2 и т. Д.),
  • Этот класс должен содержать метод mul, который принимает другое отношение и возвращает результат умножения текущего отношения и заданного.

Я попытался использовать классы данных, которые выглядели подходящими для этой задачи, но я застрял в неспособности определить пользовательский конструктор (как числитель, так и знаменатель нужно удалить в их GCD).

Возможное решение:

 class Ratio(num : Int, denom : Int) { val numerator = num / gcd(num, denom) val denominator = denom / gcd(num, denom) // GCD calculated twice! } 

Каков самый простой способ определить конструктор класса так, чтобы GCD вычислялся один раз?

ОБНОВИТЬ

Хорошо, похоже, я нашел возможное решение:

 data class Ratio(num : Int, denom : Int) { val numerator : Int val denominator : Int { val gcd = calcGcd(num, denom) numerator = num / gcd denominator = denom / gcd } } 

но он делает этот классификатор данных бесполезным – после этого изменения класса Ratio больше не генерируется автоматически equals / hashCode / toString.

Проверено на последней версии Kotlin – 0.9.66

Программа, которая воспроизводит это поведение:

 data class Ratio(num : Int, denom : Int) { val numerator : Int val denominator : Int { val gcd = BigInteger.valueOf(num.toLong()).gcd(BigInteger.valueOf(denom.toLong())).intValue(); numerator = num / gcd; denominator = denom / gcd } } data class Ratio2(val num : Int, val denom : Int) fun main(args: Array<String>) { println("r = " + Ratio(1, 6).toString()) println("r2 = " + Ratio2(1, 6).toString()) } 

вывод:

 r = Ratio@4ac68d3e r2 = Ratio2(num=1, denom=6) 

ясно, что Ratio больше не имеет автоматического сгенерированного метода toString

Хорошо, я нашел ответ (спасибо Андрею, который указал на необходимость иметь частный ctor в описанном случае использования):

 data class Ratio private (val numerator : Int, val denominator : Int) { class object { fun create(numerator : Int, denominator : Int) : Ratio { val gcd = BigInteger.valueOf(numerator.toLong()).gcd(BigInteger.valueOf(denominator.toLong())).intValue(); return Ratio(numerator / gcd, denominator / gcd) } } } 

по какой-либо причине «идентификатор данных» будет бесполезен, если в классе используются блоки инициализатора, поэтому, если вы хотите иметь собственную логику построения и сохранить автоматически созданные методы hashCode / equals / toString, вам нужно будет использовать заводские методы.

Как насчет:

 class Ratio(num : Int, denom : Int) { private val theGcd = gcd(num, denom) val numerator = num / theGcd val denominator = denom / theGcd } 

EDIT: справедливая точка о бесполезной области. Альтернативой может быть использование объекта с ленивой оценкой. См. Документы здесь http://kotlinlang.org/docs/reference/delegated-properties.html

Вот (непроверенный) пойти на это ..

 import kotlin.properties.Delegates class Ratio(num : Int, denom : Int) { private val theGcd: Int by Delegates.lazy { gcd(num, denom) } val numerator = num / theGcd val denominator = denom / theGcd } 
Intereting Posts
Каков наилучший способ обработки нулевых ситуаций в Kotlin при расширении класса Java? Не удалось получить неизвестное свойство «anko_version» для объекта типа …? добавление фрагмента в действие из фрагмента, который не работает в Pixel XL android версии 8.0.0 TextClock в AppWidget не отвечает на вызовы функций Как принудительно ввести пустую, ненулевую строку в Котлин? Kotlin on android: Не удается найти основной объединенный манифест. Что вызывает эту ошибку? Предоставлять насмешливый объект другому конструктору конструктивного объекта? Что именно создает Regex с помощью Regex.fromLiteral ()? Какой путь лучше между сопутствующим объектом и потехой без класса в Котлин? Использование kotlin-stdlib 1.1.51, но загрузка kotlin-runtime 1.3 Как обрабатывать исключения в Котлине? Android Studio ярлык для автозаполнения Toast (Kotlin) Kotlin: Как дочерний класс может использовать функцию расширения родителя в вызове супер-конструктора? Котлин создает внутренний класс для лямбда Добавление / Query / Parse SQLite с использованием Anko