Example7.1

7.1 ケースクラスとケースオブジェクト

ケースクラス ケースオブジェクト は普通のクラスやオブジェクトのように定義しますが、定義に修飾子 case が手前に付くことだけが違います。たとえば定義

abstract class Expr 
case class Number(n: Int) extends Expr 
case class Sum(e1: Expr, e2: Expr) extends Expr 

は、Number と Sum をケースクラスとして導入します。クラスやオブジェクト定義の前の case 修飾子には、次のような効果があります。

1. ケースクラスは暗黙のうちにコンストラクタ関数を伴い、それはクラスと同じ名前です。この例の場合、2つの関数

def Number(n: Int) = new Number(n) 
def Sum(e1: Expr, e2: Expr) = new Sum(e1, e2) 

が追加されます。したがって式の木をもう少し簡潔に、次のように構成できます。

Sum(Sum(Number(1), Number(2)), Number(3))

2. ケースクラスとケースオブジェクトは暗黙のうちにメソッド toString、equals、hashCode を伴い、それらはクラス AnyRef の同名のメソッドをオーバーライドします。これらのメソッド実装では、それぞれのケースクラスのメンバ構造を考慮しています。toString メソッドは式の木が構成された方法を表します。したがって、

Sum(Sum(Number(1), Number(2)), Number(3))

は、この文字列そのままに変換されます。一方、クラス AnyRef のデフォルトの実装は、一番外側のコンストラクタの名前 Sum と数字からなる文字列を返すでしょう。equals メソッドはケースクラスの2つのケースメンバを、もし同じコンストラクタで構築され、かつ、それらの引数がそれぞれ等しいなら、等しいと扱います。これは == と != の実装にも影響しますが、それらは Scala では equals を用いて実装されているからです。したがって

Sum(Number(1), Number(2)) == Sum(Number(1), Number(2))

は、true を返します。もし Sum や Number がケースクラスでないなら、同じ式は false を返します。なぜならクラス AnyRef の equals の標準実装では、異なるコンストラクタ呼び出しで生成されたオブジェクトは常に異なる、と扱うからです。hashCode メソッドも他の2つのメソッドと同じ原則に従います。デフォルトの hashCode の実装ではハッシュコードを、オブジェクトのアドレスから計算する代わりに、ケースクラスのコンストラクタ名と、コンストラクタ引数のハッシュコードから計算します。

3. ケースクラスは暗黙のうちに、パラメータなしのアクセサメソッドを伴い、それはコンストラクタ引数を読み出します。例では、Number はアクセサメソッド

def n: Int 

を持ち、コンストラクタパラメータ n を返します。一方 Sum は2つのアクセサメソッドを持ちます。

def e1: Expr, e2: Expr 

したがって、たとえば型 Sum の値 s に対して、左オペランドにアクセスするために s.e1 と書けます。しかし、型 Expr の値 e に対して項 e.e1 は正しくありませんが、それは e1 は Sum で定義されているのであって基底クラス Expr のメンバではないからです。では、静的型が基底クラス Expr である値に対してどうやってコンストラクタを判別してコンストラクタ引数にアクセスすれば良いのでしょうか? これはケースクラスの四番目にして最後の特徴によって解決されます。

4. ケースクラスを使えば、ケースクラスのコンストラクタを参照する パターン を構築できます。


名前:
コメント:

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2011年02月24日 08:39
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。