探索Scala(4)--Case Classes

5562 ワード

本文は簡単にScala言語Case Classの実現メカニズムを検討する
Case Class
Case ClassはScala言語モードマッチング機能の基礎である.クラスを定義するときにcaseキーを付けると、Case Classになります.たとえば、次のような簡単なクラスCCです.
case class CC(x: Int, y: Int)
それに加えて
caseキーワードはクラスにとって、いったい何を意味しているのでしょうか.以下、詳細に説明する.
単一オブジェクト
CCをコンパイルすると、2つのclass:CCが生成されます.classとCC$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を1つに分解します
Option.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))
    } 

}

参考資料
<>第2版