1. 程式人生 > >探索Scala(4)-- Case Classes

探索Scala(4)-- Case Classes

本文簡單探討一下Scala語言Case Class的實現機制

Case Class

Case Class是Scala語言模式匹配功能的基礎。如果定義類的時候加上case關鍵字,那麼它就變成了Case Class,比如下面這個簡單的類CC:

case class CC(x: Int, y: Int)
那麼加上case關鍵字對於一個類來說,到底意味著什麼呢?下文將進行詳細的解釋。

單例物件

編譯CC,會產生兩個class:CC.classCC$.class。這說明,Scala會給case類自動新增一個單例物件。下面是反編譯CC$.class之後的(部分)程式碼:

public final class CC$ extends scala.runtime.AbstractFunction2 implements scala.Serializable {
    
    public static final CC$ MODULE$;

    static {
        new CC$();
    }

    private CC$() {
        MODULE$ = this;
    }

    public Object readResolve() {
        return MODULE$;
    }
    ...
}

apply()和unapply()方法

CC$定義了apply()方法,這樣我們就可以不用敲new關鍵字,而是通過下面這樣的方式來建立CC例項:

val cc = CC(2, 16)
CC$還定義了一個和apply()互補的方法:unapply(),它把CC拆成一個Option<Tuple>。apply()和unapply()方法的程式碼如下所示:
public final class CC$ ... {
    ...
    public CC apply(int x, int y) {
        return new CC(x, y);
    }

    public scala.Option<scala.Tuple2<Object, Object>> unapply(CC cc) {
        if (cc == null) {
            return scala.None$.MODULE$;
        }
        return new scala.Some(new scala.Tuple2$mcll$sp(cc.x, cc.y))
    } 

}

Case Class預設是Immutable

Case類的欄位,預設會被編譯器加上val關鍵字,也就是說,CC類實際上是下面這樣:

case class CC(val x: Int, val y: Int)
下面是CC.class反編譯之後的相應程式碼:
public class CC implements scala.Product, scala.Serializable {
    
    private final int x;
    private final int y;

    public CC(int x, int y) {
        this.x = x;
        this.y = y;
        scala.Product$class.$init$(this);
    }

    public int x() {
        return x;
    }
    public int y() {
        return y;
    }

}

toString()、hashCode()和equals()方法

Scala編譯器負責(根據欄位)給Case類重寫toString()、hashCode()和equals()方法。

copy()方法

copy()方法讓Case類例項可以完整的,或者有少量變化的複製自己。如下面的程式碼所示:

val cc = CC(1, 2)
val cc1 = cc.copy()
val cc2 = cc.copy(y = 8) // Named arguments
上面的程式碼去掉語法糖之後,實際上是下面這樣:
val cc = CC(1, 2)
val cc1 = cc.copy(cc.copy$default$1(), cc.copy$default$2())
val cc2 = cc.copy(cc.copy$default$1(), 8)
下面是反編譯之後copy()和copy$default$n()方法程式碼:
public class CC ... {
    ...
    public CC copy(int x, int y) {
        return new CC(x, y);
    }
    public int copy$default$1() {
        return x();
    }
    public int copy$default$2() {
        return y();
    }
    ...
}

實現Product和Serializable介面

Case類還實現了scala.Product和scala.Serializable介面(Product和Serializable實際上都是Traits)。

完整的反編譯程式碼

下面是CC.class和CC$.class的完整程式碼,僅供參考:

public class CC implements scala.Product, scala.Serializable {
    
    private final int x;
    private final int y;

    public CC(int x, int y) {
        this.x = x;
        this.y = y;
        scala.Product$class.$init$(this);
    }

    public int x() {
        return x;
    }
    public int y() {
        return y;
    }

    public int hashCode() {...}
    public boolean equals(Object) {...}
    public String toString() {...}

    public boolean canEqual(Object obj) {
        return obj instanceof CC;
    }

    public CC copy(int x, int y) {
        return new CC(x, y);
    }
    public int copy$default$1() {
        return x();
    }
    public int copy$default$2() {
        return y();
    }

    // Product
    // def productArity: Int
    // def productElement(n: Int): Any
    // def productIterator: Iterator[Any]
    // def productPrefix = ""
    
    public int productArity() {
        return 2;
    }
    public Object productElement(int n) {
        switch (n) {
            case 0: return x();
            case 1: return y();
            default: throw new IndexOutOfBoundsException();
        }
    }
    public scala.collection.Iterator productIterator() {
        return scala.runtime.ScalaRunTime$.MODULE$.typedProductIterator(this);
    }
    public String productPrefix() {
        return "CC";
    }

    public static CC apply(int x, int y) {
        return CC$.MODULE$.apply(x, y);
    }
    public static scals.Option<scala.Tuple2<Object, Object>> unapply(CC cc) {
        return CC$.MODULE$.unapply(cc);
    }
    public scala.Function1 static tupled() {
        return CC$.MODULE$.tupled();
    }
    public scala.Function1 static curried() {
        return CC$.MODULE$.curried();
    }

}
public final class CC$ extends scala.runtime.AbstractFunction2 implements scala.Serializable {
    
    public static final CC$ MODULE$;

    static {
        new CC$();
    }

    private CC$() {
        MODULE$ = this;
    }

    public Object readResolve() {
        return MODULE$;
    }

    public String toString() {
        return "CC";
    }

    public CC apply(int x, int y) {
        return new CC(x, y);
    }

    public scala.Option<scala.Tuple2<Object, Object>> unapply(CC cc) {
        if (cc == null) {
            return scala.None$.MODULE$;
        }
        return new scala.Some(new scala.Tuple2$mcll$sp(cc.x, cc.y))
    } 

}

參考資料

<<Programming in Scala>>第二版