unapply method of a case class is not used by the Scala compiler to do pattern matching, why is that?

坚强是说给别人听的谎言 提交于 2019-12-01 06:42:00

问题


abstract class Animal

case class Cat(name: String) extends Animal

case class Dog(name: String) extends Animal

Say I have defined Cat and Dog, two case classes.

Then I use them like this:

val animal = createAnimal
animal match {
  case Dog(anyName) => "this is a dog"
  case Cat("kitty") => "this is a cat named kitty"
  case _ => "other animal"
}

If I decompile the bytecode to Java, I get something like this:

Animal animal = createAnimal();
String result = "other animal";

if (animal instanceof Dog) {
    result = "this is a dog";
} else if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    if (cat.name() == "kitty") {
        result = "this is a cat named kitty";
    }
}

return result;

The compiler generates unapply methods for both Cat and Dog, but they are not used in the pattern matching code.

Why is that?


回答1:


Looking at this question from the point of view of the Scala language, the implementation works as specification requires. See http://www.scala-lang.org/docu/files/ScalaReference.pdf

In §5.3.2, case classes are defined to include an implementation of unapply in the companion (extractor) object.

However, when we get to pattern matching (§8.1), case classes have their own section on matching, §8.1.6, which specifies their behaviour in pattern matching based on the parameters to the constructor, without any reference to the already-generated unapply/unapplySeq:

8.1.6 Constructor Patterns

Syntax:

SimplePattern ::= StableId ‘(’ [Patterns] ‘)

A constructor pattern is of the form c(p1,…,pn) where n≥0. It consists of a stable identifier c, followed by element patterns p1,…,pn. The constructor c is a simple or qualified name which denotes a case class. If the case class is monomorphic, then it must conform to the expected type of the pattern, and the formal parameter types of x's primary constructor are taken as the expected types of the element patterns p1,…,pn. If the case class is polymorphic, then its type parameters are instantiated so that the instantiation of c conforms to the expected type of the pattern. The instantiated formal parameter types of c's primary constructor are then taken as the expected types of the component patterns p1,…,pn. The pattern matches all objects created from constructor invocations c(v1,…,vn) where each element pattern pi matches the corresponding value vi.

The document continues to describe the use of unapply/unapplySeq in §8.1.8; but this is a separate, disjoint, part of the specification, which is applied for classes which are not case classes.

So you can consider unapply to be a useful method to use in your own code, but not something that is required by pattern matching inside the scala language.




回答2:


My guess is that this is an optimisation scalac performs. The unapply method is synthetic, so the compiler knows its implementation and might improve on the runtime performance.

If that theory is correct, the following should be different:

object Cat {
  def unapply(c: Cat): Option[String] = Some(c.name)
}
class Cat(val name: String) extends Animal


来源:https://stackoverflow.com/questions/24227037/unapply-method-of-a-case-class-is-not-used-by-the-scala-compiler-to-do-pattern-m

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!