Search code examples
scalaconstructorassignment-operator

How to create custom assignment operator in Scala


I'm trying to create a custom data type that behaves like an Int, but has certain specific behavior and typing (eg., it has to be positive, it has to fit within the range of our database's 'integer' type, etc).

To make it a friendly class, I want to have custom assignment operators, etc., for instance I'd like the following to all work:

val g: GPID = 1   // create a GPID type with value 1
val g: GPID = 1L  // take assignment from a Long (and downcast into Int)
if (g == 1) ...   // test the value of GPID type against an Int(1)

This is what I have so far but I'm not getting the expected behavior:

case class GPID(value: Int) extends MappedTo[Int] {
    require(value >= 1, "GPID must be a positive number")
    require(value <= GPDataTypes.integer._2, s"GPID upper bound is ${GPDataTypes.integer._2}")

    def this(l: Long) = this(l.toInt)

    def GPID = value
    def GPID_=(i: Int) = new GPID(i)
    def GPID_=(l: Long) = new GPID(l.toInt)

    override def toString: String = value.toString

    override def hashCode:Int = value

    override def equals(that: Any): Boolean =
        that match {
            case that: Int => this.hashCode == that.hashCode
            case that: Long => this.hashCode == that.hashCode
            case _ => false
        }
}

object GPID {
    implicit val writesGPID = new Writes[GPID] {
        def writes(g: GPID): JsValue = {
            Json.obj(
                "GPID" -> g.value
            )
        }
    }

    implicit val reads: Reads[GPID] = (
        (__ \ "GPID").read[GPID]
        )

    def apply(l: Long) = new GPID(l.toInt)

    implicit def gpid2int(g: GPID): Int = hashCode

    implicit def gpid2long(g: GPID): Long = hashCode.toLong
}

The problems I have are:

  1. Assignment doesn't work, for instance: val g: GPID = 1

  2. Implicit conversion is not working, for instance: val i: Int = g

Any help would be appreciated... haven't build a custom type like this before so overriding assignment and implicit conversion is new to me...


Solution

  • object TestInt extends App {
    
      class GPID(val value: Int) {
        require(value >= 1, "GPID must be a positive number")
        require(value <= 10, s"GPID upper bound is 10")
    
        override def equals(that: Any) = value.equals(that)
    
        override def toString = value.toString
    
        // add more methods here (pimp my library)
      }
    
      implicit def fromInt(value: Int) = new GPID(value)
    
      implicit def fromInt(value: Long) = new GPID(value.toInt) //possible loss of precision
    
      val g: GPID = 1
      val g2: GPID = 1L
    
      if (g == 1)
        println("ONE: " + g)
      else
        println("NOT ONE: " + g)
    }
    

    Prints:

    ONE: 1