1. 程式人生 > >話說模式匹配(6) case類的細節

話說模式匹配(6) case類的細節

我們在第二篇文章裡曾提到過:

本質上case class是個語法糖,對你的類構造引數增加了getter訪問,還有toString, hashCode, equals 等方法; 最重要的是幫你實現了一個伴生物件,這個伴生物件裡定義了apply方法和unapply方法。

現在我們來詳細的分析一下case class,對一個簡單的樣本類

case class B()

反編譯後看到編譯器自動給它混入了Product特質,以及Serializable特質:

public class B implements scala.Product,scala.Serializable {
    public B copy();
    public java.lang.String productPrefix();
    public int productArity();
    public java.lang.Object productElement(int);
    public scala.collection.Iterator<java.lang.Object> productIterator();
    public boolean canEqual(java.lang.Object);
    public int hashCode();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public B();
}

再看看它的半生物件:

//伴生物件也混入了AbstractFunction0 和 Serializable 特質
public final class B$ extends scala.runtime.AbstractFunction0<B> implements scala.Serializable {
    public static final B$ MODULE$;
    public static {};
    public final java.lang.String toString();
    public B apply();
    public boolean unapply(B);
    public java.lang.Object apply();
}

通過反編譯的結果我們瞭解到了幾點:

  1. 編譯器對case類混入了Product特質
  2. 編譯器對case類增加了copy方法;
  3. 編譯器對case類實現了equals/hashCode/toString等方法
  4. 伴生物件中最重要的方法是 unapply 這個方法是在進行構造器模式匹配時的關鍵。
  5. 伴生物件中apply方法則為建立物件提供方便,相當於工廠方法。
  6. 伴生物件繼承了AbstractFunction

從case類的設計目的來看,最重要的是提供構造器模式匹配(且構造時的引數,與解構的結果一致),另外case類可看作是資料物件,不可變的資料物件。

因為case類封裝的資料有不變的特點,以及可以進行模式匹配,所以它在actor中經常使用,很適合封裝訊息在actor之間傳遞。

上面列出的幾點中,對於第6點“伴生物件繼承自 Function”可能感到奇怪,Martin在這裡回答了為什麼case類的伴生物件會繼承FunctionN

The reason why case class companion objects implement FunctionN is that before, case classes generated a class and a factory method, not a companion object. When we added extractors to Scala it made more sense to turn the factory method into a full companion object with apply and unapply methods. But then, since the factory method did conform to FunctionN, the companion object needed to conform, too.

另外,當引數大於2個時,FunctionN 都提供了tupled方法生成一個函式,該函式可以接受一個tuple作為引數構造出結果,比如:

scala> case class A(x: Int, y:Int)

scala> A.tupled
res11: ((Int, Int)) => A = <function1>

scala> val t = (100,100)
t: (Int, Int) = (100,100)

scala> A.tupled(t)
res9: A = A(100,100)