5.3 クラス定義 (Class Definitions)


   TmplDef            ::= 'class' ClassDef
   ClassDef           ::= id [TypeParamClause] {Annotation}
                          [AccessModifier] ClassParamClauses ClassTemplateOpt
   ClassParamClauses  ::= {ClassParamClause}
                          [[nl] '(' implicit ClassParams ')']
   ClassParamClause   ::= [nl] '(' [ClassParams] ')'
   ClassParams        ::= ClassParam {',' ClassParam}
   ClassParam         ::= {Annotation} [{Modifier} ('val' | 'var')]
                          id [':' ParamType] ['=' Expr]
   ClassTemplateOpt   ::= 'extends' ClassTemplate | [['extends'] TemplateBody]


   class c[tps] as m(ps1)...(psn) extends t    (n >= 0)


c は定義されるクラスの名前です。

tps is a non-empty list of type parameters of the class being defined. The scope of a type parameter is the whole class definition including the type parameter section itself. It is illegal to define two type parameters with the same name. The type parameter section [tps] may be omitted. A class with a type parameter section is called polymorphic, otherwise it is called monomorphic.

tps は定義されるクラスの型パラメータの非空リストです。 型パラメータのスコープは、それ自身の型パラメータ部を含むクラス定義全体です。 同じ名前の 2 つの型パラメータを定義することは不正です。 型パラメータ部 [tps] は省略されるかもしれません。 型パラメータ部をもつクラスは 多相的(polymorphic) と呼ばれ、 そうでなければ、 単相的(monomorphic) と呼ばれます。

as is a possibly empty sequence of annotations (§11). If any annotations are given, they apply to the primary constructor of the class.

as はアノテーション(§11)の並びで、 空きでも構いません。 もしアノテーションが与えられていれば、 それらはクラスの基本コンストラクタへ適用されます。

m is an access modifier (§5.2) such as private or protected, possibly with a qualification. If such an access modifier is given it applies to the primary constructor to the class.

m は private/protected のようなアクセス修飾子 (§5.2)で、 限定修飾子をともなうかもしれません。 もしそのようなアクセス修飾子が与えられていれば、 クラスの基本コンストラクタへ適用されます。

(ps1) ... (psn) are formal value parameter clauses for the primary constructor of the class. The scope of a formal value parameter includes all subsequent parameter sections and the template t . However, a formal value parameter may not form part of the types of any of the parent classes or members of the class template t . It is illegal to define two formal value parameters with the same name. If no formal parameter sections are given, an empty parameter section () is assumed.

(ps1)...(psn) は、クラスの 基本コンストラクタ(primary constructor) の 形式上の値パラメータ節です。 形式上の値パラメータのスコープは、 後に続くすべてのパラメータ部とテンプレート t を含みます。 しかし、形式上の値パラメータは、いかなる親クラスの、あるいはクラステンプレート t のメンバーの型部分も形成しません。 同じ名前をもつ 2 つの形式上の値パラメータを定義することは不正です。 もし形式上のパラメータ部が与えられていないなら、 空きのパラメータ部 () が想定されます。

If a formal parameter declaration x : T is preceded by a val or var keyword, an accessor (getter) definition (§4.2) for this parameter is implicitly added to the class. The getter introduces a value member x of class c that is defined as an alias of the parameter. If the introducing keyword is var, a setter accessor x _= (§4.2) is also implicitly added to the class. In invocation of that setter x _=(e) changes the value of the parameter to the result of evaluating e. The formal parameter declaration may contain modifiers, which then carry over to the definition(s). A formal parameter prefixed by val or var may not at the same time be a call-by-name parameter (§4.6.1). t is a template (§5.1) of the form

もし形式上のパラメータ宣言 x : T に val または var キーワードが先行するなら、 このパラメータ用のアクセス子(ゲッター)定義 (§4.2) が暗黙のうちにクラスに加えられます。 ゲッターは、パラメータのエイリアスとして定義される、 クラス c の値メンバー x を導入します。 もし導入するキーワードが var なら、 セッターアクセス子 x _= (§4.2)も暗黙のうちにクラスに加えられます。 セッターの呼び出しにおいて、x _=(e) は、パラメータの値を e の評価結果に変えます。 形式上のパラメータ宣言は修飾子を含むことができ、 それはアクセス子定義へ持ち越されます。 val または var が前置された形式上のパラメータは、 同時に名前呼び出しパラメータ (§4.6.1)にはできません。

t は次の形のテンプレート(§5.1)です。

   sc with mt1 with ... with mtm { stats } (m >= 0)

これは、基底クラス、そのクラスのオブジェクトの初期状態と振る舞いを定義します。 継承節 extends sc with mt1 with ... with mtm は、省略されるかもしれません。 その場合、extends scala.AnyRef が想定されます。 クラス本体 {stats} も同じく省略されるかもしれません。その場合は、空の本体 {} が想定されます。

This class definition defines a type c[tps] and a constructor which when applied to parameters conforming to types ps initializes instances of type c[tps] by evaluating the template t .

このクラス定義は、型 c[tps] とコンストラクタを定義し、そのコンストラクタは、 型 ps に適合するパラメータへ適用されるときにテンプレート t を評価して、 型 c[tps] のインスタンスを初期化します。

Example 5.3.1 : 次の例は、クラス C の val と var パラメータを示します:

   class C(x: Int, val y: String, var z: List[String])
   val c = new C(1, "abc", List())
   c.z = c.y :: c.z

Example 5.3.2 : 次のクラスは、そのコンパニオンモジュールからのみ生成できます。

   object Sensitive {
     def makeSensitive(credentials: Certificate): Sensitive =
       if (credentials == Admin) new Sensitive()
       else throw new SecurityViolationException
   class Sensitive private () {

5.3.1 コンストラクタ定義 (Constructor Definitions)


   FunDef         ::= 'this' ParamClause ParamClauses
                      ('=' ConstrExpr | [nl] ConstrBlock)
   ConstrExpr     ::= SelfInvocation
                    | ConstrBlock
   ConstrBlock    ::= '{' SelfInvocation {semi BlockStat} '}'
   SelfInvocation ::= 'this' ArgumentExprs {ArgumentExprs}

クラスは基本コンストラクタのほかに、追加のコンストラクタを持てます。 それらは def this(ps1)...(psn) = e の形のコンストラクタ定義で定義できます。 このような定義は、取り囲むクラスに対し、形式上のパラメータリスト ps1,...,psn として与えられたパラメータと、 その評価がコンストラクタ式 e で定義された追加のコンストラクタを導入します。 各形式上のパラメータのスコープは、後に続くパラメータ部とコンストラクタ式 e です。 コンストラクタ式は、自己コンストラクタ呼び出し this(args1)...(argsn) 、あるいは自己コンストラクタ呼び出しで始まるブロックです。 自己コンストラクタ呼び出しは、 クラスのジェネリックなインスタンスを構築しなくてはなりません。 すなわち、もし問題のクラスが名前 C と型パラメータ [tps] をもつなら、自己コンストラクタ呼び出しは C[tps] のインスタンスを生成しなくてはなりません。; 形式上の型パラメータをインスタンス化することは許されていません。

The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class and by any early definitions (§5.1.6) of the enclosing template. The rest of the constructor expression is type-checked and evaluated as a function body in the current class .

コンストラクタ定義のシグニチャと自己コンストラクタ呼び出しは、 取り囲むクラス定義の実際の場所のスコープ内で、型チェックおよび評価されます。 また、取り囲むクラスのすべての型パラメータと 取り囲むテンプレートのすべての事前定義 (§5.1.6)によって拡張されます。 コンストラクタ式の残りは、現在のクラス内の関数本体として型チェックされ、 評価されます。

If there are auxiliary constructors of a class C , they form together with C's primary constructor (§5.3) an overloaded constructor definition. The usual rules for overloading resolution (§6.26.3) apply for constructor invocations of C , including for the self constructor invocations in the constructor expressions themselves. However , unlike other methods, constructors are never inherited. To prevent infinite cycles of constructor invocations, there is the restriction that every self constructor invocation must refer to a constructor definition which precedes it (I.e. it must refer to either a preceding auxiliary constructor or the primary constructor of the class) .

もしクラス C の補助コンストラクタがあれば、それらは C の基本コンストラクタ (§5.3)と共に、 オーバーロードされたコンストラクタ定義を形成します。 オーバーロード解決(§6.26.3) の通常の規則が、クラス C のコンストラクタ呼び出しに、 それ自身のコンストラクタ式中の自己コンストラクタ呼び出しの場合も含めて、 適用されます。 しかし他のメソッドと異なり、コンストラクタは決して継承されません。 コンストラクタ呼び出しの無限ループを防ぐために、 すべての自己コンストラクタ呼び出しは、 それに先行するコンストラクタ定義を参照しなければならないという制約があります (すなわち、先行する補助コンストラクタあるいはクラスの基本コンストラクタの いずれかを参照しなくてはなりません)。

Example 5.3.3 : 次のクラス定義について考えます。

   class   LinkedList[A]() {
     var   head = _
     var   tail = null
     def   isEmpty = tail != null
     def   this(head: A) = { this(); this.head = head }
     def   this(head: A, tail: List[A]) = { this(head); this.tail = tail }

これは 3 つのコンストラクタをもつクラス LinkedList を定義しています。 2 番目のコンストラクタはシングルトンリストを構築し、他方、3 番目は 与えられた head と tail をもつリストを構築します。

5.3.2 ケースクラス (Case Classes)


   TmplDef ::=     'case' 'class' ClassDef

If a class definition is prefixed with case, the class is said to be a case class.

The formal parameters in the first parameter section of a case class are called elements ; they are treated specially. First, the value of such a parameter can be extracted as a field of a constructor pattern. Second, a val prefix is implicitly added to such a parameter, unless the parameter carries already a val or var modifier. Hence, an accessor definition for the parameter is generated (§5.3) .

クラス定義の前に case が置かれていると、クラスはケースクラスと言われます。

ケースクラスの最初のパラメータ部中の形式上のパラメータは、 要素(elements) と呼ばれます;それらは特別に扱われます。 第一に、そのようなパラメータの値は、 コンストラクタパターンのフィールドとして抽出できます。 第二に、パラメータに既に val または var 修飾子がついていなければ、 val 前置子がそのようなパラメータに暗黙の内に加えられます。 ですから、パラメータのアクセス子定義が生成されます (§5.3)。

A case class definition of c[tps](ps1)...(psn) with type parameters tps and value parameters ps implicitly generates an extractor object (§8.1.8) which is defined as follows:

型パラメータ tps と値パラメータ ps をもつケースクラス定義 c[tps](ps1)...(psn) は、次のような、 抽出子オブジェクト(§8.1.8)の定義を 暗黙のうちに生成します。

   object c {
     def apply[tps](ps1)...(psn): c[tps] = new c[Ts](xs1)...(xsn)
     def unapply[tps](x : c[tps]) =
       if (x eq null) scala.None
       else scala.Some(x.xs11,...,x.xs1k)

Here, Ts stands for the vector of types defined in the type parameter section tps, each xsi denotes the parameter names of the parameter section psi , and xs11,...,xs1k denote the names of all parameters in the first parameter section xs1 . If a type parameter section is missing in the class, it is also missing in the apply and unapply methods. The definition of apply is omitted if class c is abstract .

ここで、Ts は型パラメータ部 tps で定義された型のベクトル(一次元配列)を表し、 各 xsi はパラメータ部 psi のパラメータ名を示し、xs11,...,xs1k は 最初のパラメータ部 xs1 中のすべてのパラメータの名前を示します。 もし型パラメータ部がクラスにないなら、それは apply および unapply メソッドにもありません。 もしクラス c が abstract なら、apply の定義は省略されます。

If the case class definition contains an empty value parameter list, the unapply method returns a Boolean instead of an Option type and is defined as follows:

もしケースクラス定義が空の値パラメータリストをもつなら、 unapply メソッドはオプション型の代わりに Boolean を返し、 次のように定義されます。:

   def unapply[tps](x : c[tps]) = x ne null

The name of the unapply method is changed to unapplySeq if the first parameter section ps1 of c ends in a repeated parameter of (§4.6.2). If a companion object c exists already, no new object is created, but the apply and unapply methods are added to the existing object instead .

もし c の最初のパラメータ部 ps1 が反復パラメータ (§4.6.2)で終わるなら、 unapply メソッドの名前は unapplySeq に変わります。 もしコンパニオンオブジェクト c が既に存在するなら、 新しいオブジェクトは生成されず、 代わりに apply および unapply メソッドが既存のオブジェクトに加えられます。

A method named copy is implicitly added to every case class unless the class already has a member (directly defined or inherited) with that name. The method is defined as follows:

copy と名付けられたメソッドが、クラスがそういう名前の(直接定義されたか、 あるいは継承した)メンバーを持たない限り、 暗黙のうちにすべてのケースクラスに加えられます。 メソッドは次のように定義されます:

   def copy[tps](ps´1)...(ps´n): c[tps] = new c[Ts](xs1)...(xsn)

Again, Ts stands for the vector of types defined in the type parameter section tps and each xsi denotes the parameter names of the parameter section ps´i . Every value parameter ps´i,j of the copy method has the form xi,j :Ti,j =this.xi,j , where xi,j and Ti,j refer to the name and type of the corresponding class parameter psi,j .

ここで再び、Ts は型パラメータ部 tps で定義された型のベクトルを表し、 各 xsi はパラメータ部 ps´i のパラメータ名を示します。 copy メソッドのすべての値パラメータ ps´ i,j は、 x i,j :T i,j =this.x i,j の形をしています。ここで、x i,j と T i,j は、 対応するクラスパラメータ ps i,j の名前と型を参照します。

Every case class implicitly overrides some method definitions of class scala.AnyRef (§12.1) unless a definition of the same method is already given in the case class itself or a concrete definition of the same method is given in some base class of the case class different from AnyRef. In particular:

すべてのケースクラスは、クラス scala.AnyRef (§12.1) のいくつかのメソッド定義を暗黙のうちにオーバライドします。 ただしそれは、同じ名前のメソッド定義がケースクラス自身で既に与えられていないか、 あるいは同じ名前のメソッドの具象定義がケースクラスの AnyRef 以外の基底クラス中で与えられていない場合です。 特に:

Method equals: (Any)Boolean is structural equality, where two instances are equal if they both belong to the case class in question and they have equal (with respect to equals) constructor arguments .

メソッド equals:(Any)Boolean は構造的な等価(structural equality)を表します。 ここで 2 つのインスタンスは、もしそれらが両方とも問題のケースクラスに属し、 そしてそれらが (equals に関して) 等価なコンストラクタ引数を持つなら、等価です。

Method hashCode: Int computes a hash-code. If the hashCode methods of the data structure members map equal (with respect to equals) values to equal hash-codes, then the case class hashCode method does too .

メソッド hashCode:Int は、ハッシュ・コードを計算します。 もしデータ構造メンバーの hashCode メソッドが、(equals に関して)等しい値を 等しいハッシュ・コードにマップするなら、ケースクラスの hashCode メソッドも 同様にマップします。

メソッド toString:String は、クラスとその要素名を含む文字列表現を返します。

Example 5.3.4 :

Here is the definition of abstract syntax for lambda calculus:


   class Expr
   case class Var   (x: String)          extends Expr
   case class Apply (f: Expr, e: Expr)   extends Expr
   case class Lambda(x: String, e: Expr) extends Expr

This defines a class Expr with case classes Var, Apply and Lambda. A call-by-value evaluator for lambda expressions could then be written as follows .

これはケースクラス Var、Apply と Lambdaをもつクラス Expr を定義します (訳注:関係が逆?)。 ラムダ式に対する値呼出し評価子は、このとき次のように 書けます。

   type Env = String => Value
   case class Value(e: Expr, env: Env)
   def eval(e: Expr, env: Env): Value = e match {
     case Var (x) =>
     case Apply(f, g) =>
       val Value(Lambda (x, e1), env1) = eval(f, env)
       val v = eval(g, env)
       eval (e1, (y => if (y == x) v else env1(y)))
     case Lambda(_, _) =>
       Value(e, env)

プログラムの他の場所で、型 Expr を拡張するケースクラスをさらに定義できます。 例えば、

   case class Number(x: Int) extends Expr

この形の拡張性は、基底クラス Expr を sealed と宣言することで排除できます。; その場合、Expr を直接拡張するすべてのクラスは、Expr と同じソースファイル中になければなりません。

5.3.3 トレイト (Traits)


   TmplDef          ::= 'trait' TraitDef
   TraitDef         ::= id [TypeParamClause] TraitTemplateOpt
   TraitTemplateOpt ::= 'extends' TraitTemplate | [['extends'] TemplateBody]

トレイトは、他のあるクラスにミックスインとして加えることを意図したクラスです。 通常のクラスと異なり、トレイトはコンストラクタパラメータを持つことは できません。 さらに、トレイトのスーパークラスにコンストラクタ引数を渡せません。 これは必要ではありません。なぜなら、 スーパークラスが初期化された後でトレイトは初期化されるからです。

Assume a trait D defines some aspect of an instance x of type C (i.e. D is a base class of C). Then the actual supertype of D in x is the compound type consisting of all the base classes in LL(C) that succeed D. The actual supertype gives the context for resolving a super reference in a trait (§6.5). Note that the actual supertype depends on the type to which the trait is added in a mixin composition; it is not statically known at the time the trait is defined .

トレイト D が、型 C のインスタンス x のある特徴を定義しているとします (つまり、D は C の基底クラス)。 このとき、x 中の D の 実際のスーパー型(actual supetype) は、 D を継承する LL(C)中のすべての基底クラスからなる複合型です。 実際のスーパー型は、トレイトにおける superの参照 (§6.5) を解決するためのコンテキストを与えます。 実際のスーパー型は、 ミックスイン合成中にトレイトが付加される型に依存することに注意してください; トレイトが定義された時点でそれを静的に知ることはできません。

If D is not a trait, then its actual supertype is simply its least proper supertype (which is statically known) .

もし D がトレイトでないなら、その実際のスーパー型は単に、 その最小固有のスーパー型です(静的に知ることができます)。

Example 5.3.5 : 次のトレイトは、 ある型のオブジェクトと比較可能にするプロパティを定義します。 これは抽象メソッド < と、他の比較演算子 <=、>、>= のデフォルト実装を含みます。

   trait Comparable[T <: Comparable[T]] { self: T =>
     def < (that: T): Boolean
     def <=(that: T): Boolean = this < that || this == that
     def > (that: T): Boolean = that < this
     def >=(that: T): Boolean = that <= this

Example 5.3.6 Consider an abstract class Table that implements maps from a type of keys A to a type of values B. The class has a method set to enter a new key / value pair into the table, and a method get that returns an optional value matching a given key. Finally, there is a method apply which is like get, except that it returns a given default value if the table is undefined for the given key.

Example 5.3.6 : キー A の型から値 B の型へのマップを実装する、抽象クラス Table について考えます。 このクラスは、 新しい キー/値 ペアをテーブルに入れるためのメソッド set と、 与えられたキーにマッチするオプション値を返すメソッド get を持ちます。 最後に、get に似たメソッド apply があり、 それは、もし与えられたキーに対してテーブルが未定義なら、 与えられたデフォルト値を返す点が get と異なります。


   abstract class Table[A, B](defaultValue: B) {
     def get(key: A): Option[B]
     def set(key: A, value: B)
     def apply(key: A) = get(key) match {
       case Some(value) => value
       case None => defaultValue

次は Table クラスの具象実装です。

   class ListTable[A, B](defaultValue: B) extends Table[A, B](defaultValue) {
     private var elems: List[(A, B)]
     def get(key: A) = elems.find(._1.==(key)).map(._2)
     def set(key: A, value: B) = { elems = (key, value) :: elems }

次は、その親クラスの get および set 操作への並行アクセスを防ぐトレイトです。

   trait SynchronizedTable[A, B] extends Table[A, B] {
     abstract override def get(key: A): B =
       synchronized { super.get(key) }
     abstract override def set((key: A, value: B) =
       synchronized { super.set(key, value) }

Table が形式上のパラメータを用いて定義されていても、 SynchronizedTable はそのスーパークラス Table に引数を渡さないことに注意してください。 SynchronizedTable の get と set メソッド内の super 呼び出しは、 クラス Table 中の抽象メソッドを静的に参照していることにも注意してください。 呼び出すメソッドが abstract override (§5.2) と印されている限り、 これは正しいです。

最終的に、次のミックスイン合成は、文字列をキーとして整数を値とする (デフォルト値 0)、同期リストテーブルを生成します。:

   object MyTable extends ListTable[String, Int](0) with SynchronizedTable

オブジェクト MyTable は、 SynchronizedTable からその get と set メソッドを継承します。 これらメソッド内の super 呼び出しは、ListTable 中の対応する実装への参照へ再束縛され、 そしてそれは MyTable 中の SynchronizedTable の実際のスーパー型です。

5.4 オブジェクト定義 (Object Definitions)


   ObjectDef      ::=   id ClassTemplate

オブジェクト定義は、新しいクラスのただ 1 つのオブジェクトを定義します。 その最も一般的な形は object m extends t です。 ここで、m は定義されるオブジェクトの名前、t は次の形のテンプレート (§5.1) です。

   sc with mt1 with ... with mtn {stats} 

これは m の基底クラス、振る舞いと初期状態を定義します。 継承節 extends sc with mt1 with ... with mtn は省略されることがあり、 その場合、scala.AnyRef が想定されます。 クラス本体 {stats} も省略されることがあり、 その場合、空の本体 {} が想定されます。

オブジェクト定義はテンプレート t に適合するただ 1 つのオブジェクト (あるいは: モジュール )を定義します。 それは次の遅延評価値定義と、大まかに言って同じです。

   lazy val m = new sc with mt1 with ... with mtn { this: m.type => stats }

Note that the value defined by an object definition is instantiated lazily. The new m$cls constructor is evaluated not at the point of the object definition, but is instead evaluated the first time m is dereferenced during execution of the program (which might be never at all). An attempt to dereference m again in the course of evaluation of the constructor leads to a infinite loop or run-time error. Other threads trying to dereference m while the constructor is being evaluated block until evaluation is complete .

オブジェクト定義によって定義された値は、 遅れてインスタンス化されることに注意してください。 new m$cls コンストラクタは、オブジェクト定義の時点では評価されません。 しかし代わりに、m がプログラム実行中に最初に逆参照されるとき (そういうことは全くないかもしれない)、評価されます。 コンストラクタの評価途中で m の逆参照を再び試みると、無限ループあるいは 実行時エラーを招きます。 コンストラクタ評価中に m の逆参照を試みる他のスレッドは、 評価が完了するまでブロックします。

The expansion given above is not accurate for top-level objects. It cannot be because variable and method definition cannot appear on the top-level outside of a package object (§9.3). Instead, top-level objects are translated to static fields .

上記で与えられた拡張は、トップレベルのオブジェクトについては正確ではありません。 それはありません。なぜなら、変数とメソッド定義はパッケージオブジェクト (§9.3) の外側のトップレベルに現われることができないからです。 その代わり、トップレベルのオブジェクトは静的なフィールドに翻訳されます。

Example 5.4.1 Classes in Scala do not have static members; however, an equivalent effect can be achieved by an accompanying object definition E.g .

Example 5.4.1 : Scala のクラスは静的メンバを持ちません。; しかし、オブジェクト定義を随伴させることで、同様の効果を達成できます。

   abstract class Point {
     val x: Double
     val y: Double
     def isOrigin = (x == 0.0 && y == 0.0)
   object Point {
     val origin = new Point() { val x = 0.0; val y = 0.0 }

This defines a class Point and an object Point which contains origin as a member . Note that the double use of the name Point is legal, since the class definition defines the name Point in the type name space, whereas the object definition defines a name in the term namespace .

これはクラス Pointと、メンバーとして origin を含むオブジェクト Point を定義します。 名前 Point の2重使用が正しいことに注意してください。 クラス定義は名前 Point を型-名前空間内で定義し、 他方、オブジェクト定義は名前を項-名前空間内で定義するからです。

This technique is applied by the Scala compiler when interpreting a Java class with static members. Such a class C is conceptually seen as a pair of a Scala class that contains all instance members of C and a Scala object that contains all static members of C .

この方法は、静的メンバをもつ Java クラスを解釈するときに Scala コンパイラによって用いられます。 そのようなクラス C は、C のすべてのインスタンスメンバーを含む Scala クラスと、 C のすべての静的メンバーを含む Scala オブジェクトのペアとして概念的にとらえることができます。

一般に、クラスの コンパニオンモジュール はクラスと同じ名前をもつオブジェクトであり、 同じスコープとコンパイル単位で定義されます。 逆に、そのクラスはモジュールの コンパニオンクラス と呼ばれます。

最終更新:2011年02月23日 18:33


ヘルプ / FAQ もご覧ください。