gmethvin / standard-methods

Macros for implementing equals, hashCode and toString on Scala classes.

GitHub

Standard Methods

Travis CI Maven

A set of Scala macros for generating the methods defined on java.lang.Object: equals, hashCode and toString.

Getting the library

To add the dependency in sbt:

libraryDependencies += "io.methvin.standardmethods" %% "macros" % standardMethodsVersion % "provided"

where standardMethodsVersion is the latest version: latest maven version

Example

For example, suppose you have a class with:

import io.methvin.standardmethods.macros._
class Person(val name: String, val age: Int, description: String) {
  override def equals(other: Any) = equalsConstructorVals(this, other)
  override def hashCode = hashCodeConstructorVals(this)
  override def toString = toStringConstructorParams(this)
  def canEqual(other: Any): Boolean = other.isInstanceOf[Person]
}

In this case, equals and hashCode will be generated from the vals in the constructor of the class. toString is generated by referencing the constructor params in scope. The generated code looks something like this:

import io.methvin.standardmethods.macros._
class Person(val name: String, val age: Int, description: String) {

  override def equals(other: Any) = other match {
    case other$macro$1: Person =>
      other$macro$1.canEqual(this) &&
      this.name == other$macro$1.name &&
      this.age == other$macro$1.age
    case _ => false
  }

  override def hashCode = scala.util.hashing.MurmurHash3.seqHash(
    scala.collection.immutable.Seq(this.name, this.age))

  override def toString = "Person" + Seq(name, age, description).mkString("(", ",", ")")

  def canEqual(other: Any): Boolean = other.isInstanceOf[Person]
}

other canEqual this is used if it is available. Otherwise this.getClass == that.getClass is used to check if the type is compatible.

You can also use equalsAllVals and hashCodeAllVals to use all vals owned by the class rather than just those in the constructor. Note that this does not include vals defined in a superclass. Generally superclass vals can be handled by using equalsAllVals(this, that) && super.equals(that) and hashCodeAllVals(this) ^ super.hashCode. Contributions are welcome if someone wants to provide a more elegant solution.

Note: the macros are primarily meant to be used in a method of the class on which they are used, but equals and hashCode will work elsewhere as long as all the necessary methods are public. In other words, the macros for equals and hashCode themselves do not depend on the scope, since the instances used are passed directly, but the code they generate might not work due to privacy restrictions. The toStringConstructorParams macro simply references the unprefixed constructor params, so it must be used within the class where it is needed.

License

This code is licensed under the Apache License, Version 2.0.

Issues and Contributions

Please report an issue on this GitHub repository if you find anything that doesn't work as expected, or you have any feature requests. Your issue is much more likely to be fixed if you also submit a pull request to fix it yourself.