title: How to store temporary variable while initializing a Kotlin object?
body: <p>I'm studying Kotlin and as part of learning it I want to design a class that would represent a rational number, requirements:</p>
  
  <ul>
  <li>Class should contain two immutable integer fields: numerator and denominator.</li>
  <li>Class should contain valid equals, hashCode and toString implementations.</li>
  <li>When class is initialized, numerator and denominator should be deleted by their GCD (it means that <code>Ratio(1, 2) == Ratio(2, 4 /* or 4, 8 */) or Ratio(2, 4 /* or 4, 8 */).numerator == 1, .denominator == 2</code> etc.)</li>
  <li>This class should contain mul method that takes another Ratio and returns multiplication result of the current ratio and the given one.</li>
  </ul>
  
  <p>I tried to use data classes what looked suitable for that task, but I was stuck with inability to define a custom constructor (both numerator and denominator need to be deleted to their GCD).</p>
  
  <p>Possible solution:</p>
  
  <pre><code>class Ratio(num : Int, denom : Int) {
      val numerator = num / gcd(num, denom)
      val denominator = denom / gcd(num, denom) // GCD calculated twice!
  }
  </code></pre>
  
  <p>What is the simplest way to define a class constructor so that GCD is calculated once?</p>
  
  <p><strong>UPDATE</strong></p>
  
  <p>OK, it looks like I found the possible solution:</p>
  
  <pre><code>data class Ratio(num : Int, denom : Int) {
    val numerator : Int
    val denominator : Int
  
    {
      val gcd = calcGcd(num, denom)
      numerator = num / gcd
      denominator = denom / gcd
    }
  }
  </code></pre>
  
  <p>but it renders that data qualifier useless - after this change Ratio class no longer has auto generated equals/hashCode/toString.</p>
  
  <p>Verified on the latest version of Kotlin - 0.9.66</p>
  
  <p>Program that reproduces that behavior:</p>
  
  <pre><code>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&lt;String&gt;) {
    println("r = " + Ratio(1, 6).toString())
    println("r2 = " + Ratio2(1, 6).toString())
  }
  </code></pre>
  
  <p>output:</p>
  
  <pre><code>r = Ratio@4ac68d3e
  r2 = Ratio2(num=1, denom=6)
  </code></pre>
  
  <p>that's clear that Ratio no longer has auto generated toString method</p>