3

I heard that we can apply pattern matching in Scala without using case classes. Also, I do not mean just identifying the actual implementation class but also extracting various values from the object. Is it really possible? How can we achieve this?

I could not find an accepted answer that points to my solution in any of the question posted previously on similar topics. Hence, I decided to put up a new question.

MozenRath
  • 9,652
  • 13
  • 61
  • 104
  • 1
    it's a very generic question. But if a class implements the `.unapply` method, you should be able to access it's values – pedrorijo91 Dec 02 '18 at 14:25
  • 6
    Possible duplicate of [Pattern matching over non-case class in Scala](https://stackoverflow.com/questions/20031975/pattern-matching-over-non-case-class-in-scala) – mukesh210 Dec 02 '18 at 14:51

2 Answers2

6

Case classes aren't magical, what they do under the hood is generate a lot of boilerplate code.
Between that, are the apply (factory constructor) and unapply (extractor) methods on the companion object of the class.
When you try to pattern match an object, what the compiler try to do is to call the extractor method of the companion object - thus, you only need to implement your own extractor object.

For example

class User(val id: Int, val name: String)

object User {
  def apply(id: Int, name: String): User = new User(id, name)

  def unapply(user: User): Option[(Int, String)] =
    Some(user.id, user.name)
}

Now you can create a match Users this way.

val user = User(3, "Luis")
def getId(user: User): Int = user match {
  case User(id, name) => id
}
getId(user) // 3

For reference see this.

1

Arnon and Luis solutions combined for demonstration purpose:

class A(val a: Int, val b: String, val c: Int)

// Handmade companion object for demonstration purpose only.
object A {
   def unapply(u: A): Option[(Int, String, Int)] =
    Some(u.a, u.b, u.c)
}

// No factory method found, so 'new' keyword is necessary.
val a = new A(1, "hello", 3)

/* With real case class the right side expression
    calls the unapply of the Object
   otherwise call must be explicitly.
*/
val Some((b, _, c)) = A.unapply(a)
// b: Int = 1
// c: Int = 3

def getId(user: A): Int = user match {
  case A(_, _, id) => id
}

getId(a) // 3
Epicurist
  • 903
  • 11
  • 17