3
abstract class Animal

case class Cat(name: String) extends Animal

case class Dog(name: String) extends Animal

Cat と Dog という 2 つのケース クラスを定義したとします。

次に、次のように使用します。

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

バイトコードを Java に逆コンパイルすると、次のようになります。

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;

コンパイラは Cat と Dog の両方に対して unapply メソッドを生成しますが、パターン マッチング コードでは使用されません。

何故ですか?

4

2 に答える 2

4

この問題を Scala 言語の観点から見ると、実装は仕様どおりに機能します。http://www.scala-lang.org/docu/files/ScalaReference.pdfを参照してください。

§5.3.2 では、コンパニオン (エクストラクタ) オブジェクトに unapply の実装を含めるようにケース クラスが定義されています。

ただし、パターン マッチング (§8.1) に到達すると、ケース クラスにはマッチングに関する独自のセクション §8.1.6 があり、コンストラクターへのパラメーターに基づいてパターン マッチングでの動作を指定します。 unapply/unapplySeq:

8.1.6 コンストラクターのパターン

構文:

SimplePattern ::= StableId '(' [パターン] ')

コンストラクター パターンは c(p1,…,pn) の形式で、n≥0 です。これは、安定した識別子 c とそれに続く要素パターン p1,…,pn で構成されます。コンストラクタ c は、ケース クラスを示す単純な名前または修飾名です。ケース クラスが単相である場合、パターンの予想される型に準拠する必要があり、x のプライマリ コンストラクターの仮パラメーターの型は、要素パターン p1,…,pn の予想される型として取得されます。ケース クラスが多態的である場合、その型パラメーターはインスタンス化されるため、 c のインスタンス化はパターンの予想される型に準拠します。次に、c のプライマリ コンストラクターのインスタンス化された仮パラメーターの型が、コンポーネント パターン p1,…,pn の期待される型として取得されます。このパターンは、コンストラクター呼び出し c(v1,…,

ドキュメントは、§8.1.8 での unapply/unapplySeq の使用について引き続き説明しています。しかし、これは仕様の別個のばらばらな部分であり、ケース クラスではないクラスに適用されます。

したがって、unapply は独自のコードで使用する便利なメソッドであると考えることができますが、scala 言語内のパターン マッチングで必要とされるものではありません。

于 2014-06-15T11:12:50.307 に答える
2

私の推測では、これは scalac が実行する最適化です。unapplyメソッドは合成であるため、コンパイラはその実装を認識しており、実行時のパフォーマンスが向上する可能性があります。

その理論が正しければ、次の点が異なるはずです。

object Cat {
  def unapply(c: Cat): Option[String] = Some(c.name)
}
class Cat(val name: String) extends Animal
于 2014-06-15T08:26:23.687 に答える