2

Suppose I have two classes, Person and Business, that are extended by the trait Entity.

trait Entity
case class Person(name: String) extends Entity
case class Business(id: String) extends Entity

Assuming I cannot change Entity, Person and Business (they are in a different file and not to be changed) how can I define a function, say a printEntity, that prints the field name or id, depending on the entity? For example, given instances of Person and Business, how can I do something like this:

object Main extends App {
  val person1: Person = Person("Aaaa Bbbb")
  val business1: Business = Business("0001")
  
  // How can I do something like this?
  person1.printEntity // would call a function that executes println(id)
  business1.printEntity // would call a function that executes println(name)
}

Any ideas are appreciated! Sorry for the lack of context, I am still learning!

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
bruno
  • 23
  • 3
  • Scala 2 https://docs.scala-lang.org/overviews/core/implicit-classes.html Scala 3 https://docs.scala-lang.org/scala3/reference/contextual/extension-methods.html – Dmytro Mitin Oct 15 '21 at 00:57

1 Answers1

5

This is done via so called "extension methods". In scala 2 this is achieved using implicit wrapper class:

trait Entity
case class Person(name: String) extends Entity
case class Business(id: String) extends Entity

implicit class PersonWrapper(val p: Person) extends AnyVal {
  def printEntity(): Unit = {
    println(p.name)
  }
}

implicit class BusinessWrapper(val b: Business) extends AnyVal {
  def printEntity(): Unit = {
    println(b.id)
  }
}

val person1: Person = Person("Aaaa Bbbb")
val business1: Business = Business("0001")

person1.printEntity()
business1.printEntity()

// prints:
//Aaaa Bbbb
//0001

Note, x.printEntity can be called without parentheses, but, by convention, methods with Unit result type and side effects should be called with explicit empty parentheses.


UPD: As @DmytroMitin pointed out, you should extend implicit wrapper classes from AnyVal. This allows the compiler to avoid actually allocating wrapper class instances at runtime, improving performance.

Aivean
  • 10,692
  • 25
  • 39
  • 1
    You could make the implicit classes extend `AnyVal` https://docs.scala-lang.org/overviews/core/value-classes.html – Dmytro Mitin Oct 15 '21 at 00:55
  • 2
    @DmytroMitin, thank you for the reminder, I've updated my answer. – Aivean Oct 15 '21 at 01:01
  • 2
    This shouldn't be added to your answer (it's already ok, I upvoted) but just for the record there are exotic cases when an ordinary class + an implicit conversion can be better than the corresponding implicit class https://stackoverflow.com/questions/63964610/implicit-view-not-working-is-my-implicit-def-to-blame/ :) I haven't checked in the current Scala 2.13.6. Surely normally that's not the case. – Dmytro Mitin Oct 15 '21 at 01:32