seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

Scala言語仕様 2.8 リーディング - 5 クラスとオブジェクト

これはAkasaka.scala読書会の記録です

これはAkasaka.scalaでの読書会の記録です。私が事前にある程度準備しておいて、読書会当日に修正・追記を行います。

http://akskscala.github.com/

目次

  • 5.1 テンプレート(Templates)
    • 5.1.1 コンストラクタ呼び出し(Constrictor Invocations)
    • 5.1.2 クラス線形化(Class Linearization)
    • 5.1.3 クラスメンバ(Class Members)
    • 5.1.4 オーバーライド(Overriding)
    • 5.1.5 継承クロージャ(Inheritance Closure)
    • 5.1.6 事前定義(Early Definitions)
  • 5.2 修飾子(Modifiers)
  • 5.3 クラス定義(Class Definitions)
    • 5.3.1 コンストラクタ定義(Constructor Definitions)
    • 5.3.2 ケースクラス(Case Classes)
    • 5.3.3 トレイト(Traits)
  • 5.4 オブジェクト定義(Object Definitions)

5.1 テンプレート(Templates)

ClassTemplate ::=   [EarlyDefs] ClassParents [TemplateBody]
TraitTemplate ::=   [EarlyDefs] TraitParents [TemplateBody]
ClassParents  ::=   Constr {'with' AnnotType}
TraitParents  ::=   AnnotType {'with' AnnotType}
TemplateBody  ::=   [nl] '{' [SelfType] TemplateStat {semi TemplateStat} '}'
SelfType      ::=   id [':' Type] '=>'
                |    this ':' Type '=>'
テンプレート?

クラス/トレイト or オブジェクト群 or 単一のオブジェクトの型シグニチャ・ふるまい・初期状態を定義するもの。
インスタンス生成式、クラス定義とオブジェクト定義等の一部を形成する。
スーパークラスとして Java のクラス、ミックスインとして Java のインタフェースを持っていてもよい。

テンプレートの構成要素

例えば「sc with mt1 with ... with mtn {stats} 」の場合

  • sc:テンプレートのスーパークラスを定義するコンストラクタ呼び出し
  • mt1,...,mtn (n >= 0):テンプレートのトレイトを定義するトレイト参照
  • stats

テンプレートの初期化コードと追加のメンバー定義を含む文の並び。
テンプレートが abstract class や trait 定義の一部を形成する場合 abstract な型メンバを含むことができるが、抽象項メンバ(abstract term members)の宣言を含まない。
自己型(self-type) S はテンプレートを定義する型 C 自己パラメータとして与えられた型 T の最大の下限境界。

テンプレートの評価

trait かどうかによって評価のされ方が異なる。

  • trait のテンプレートの場合

ミックスイン評価は文並び stats の評価から構成される。

  • trait のテンプレートでない場合

以下の順序で評価される。

    • スーパークラスコンストラクタ sc を評価
    • sc によって表されるテンプレートのスーパークラスに至る、テンプレート線形化(5.1.1)中の全ての基底クラスをミックスイン評価、ミックスイン評価は線形化中の出現の逆順
    • 文並び stats を評価
暗黙に scala.ScalaObject への参照をもつ

あらゆるクラスの「親のリスト」は常に「scala.ScalaObject」トレイトへの参照により暗黙に拡張される。

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

scala.ScalaObject は java.lang.Object を継承しているだけのトレイト。
scala.Any や scala.AnyRef は java.lang.Object のサブ型として定義されていない)

https://github.com/scala/scala/blob/master/src/library/scala/ScalaObject.scala

最小固有のスーパー型(least proper supertype)?

そのすべての親クラス型から構成される複合型(compound type) or クラス型。

5.1.1 コンストラクタ呼び出し(Constructor Invocations)
Constr      ::=   AnnotType {'(' [Exprs] ')'

以下のいずれかを定義するもの。

  • 型、メンバー、そしてインスタンス生成式によって生成されるオブジェクトの初期状態
  • クラス/オブジェクト定義によって継承されたオブジェクト定義部分の初期状態

「x.c[targs](args1)...(argsn)」の形での関数適用となる。

x : 安定識別子(stable identifier)、省略可
c : 型名(クラスを指定するかあるいはクラスのエイリアス型を定義するかのいずれか)
targs : 型引数リスト、省略可
args1,...,argsn : 引数リスト、省略した場合暗黙に空リスト「()」が補充される

class Something[A, B](val x: A)(val y: B)
val obj = new Something[String, Int]("abc")(123)

class Something[A, B](val x: A, val y: B, z: String = "zzz")
val obj = new Something[String, Int](x = "bcd", y = 234)
5.1.2 クラス線形化(Class Linearization)

基底クラス(base classes)

"classes reachable through transitive closure of the direct inheritance relation from a class"
クラスから直接の継承関係の推移的なクロージャを通して到達可能なクラス?

"Because of mixins, the inheritance relationship on base classes forms in general directed acyclic graph."
ミックスインにより、基底クラス上の継承関係は一般に有向非巡回(非循環)グラフ(DAG)となる

DAGを含むグラフの基礎:
http://logic.cs.tsukuba.ac.jp/~kam/discrete/text/5.pdf


定義 5.1.2

C : テンプレート C1 with ... with Cn { stats } をもつクラス
LL(C) : C の線形化

LL(C) = C, LL(Cn) $ ... $ LL(C1)

ここで $ は連結を表し、右オペランドを左オペランドの同一の要素で置き換えます。

{a, A} $ B = a,(A $ B) if a ∉ B
           = A $ B     if a ∈ B

Cn と Cm であてはめてみると

{C, LL(Cn)} $ LL(Cm) = C, (LL(Cn) $ LL(Cm)) if C ∉ LL(Cm)
                     = LL(Cn) $ LL(Cm)      if C ∈ LL(Cm)

集合論の記号
http://ja.wikipedia.org/wiki/%E6%95%B0%E5%AD%A6%E8%A8%98%E5%8F%B7%E3%81%AE%E8%A1%A8#.E9.9B.86.E5.90.88.E8.AB.96.E3.81.AE.E8.A8.98.E5.8F.B7


実際に具体的な例をみてみると

Example 5.1.3

abstract class AbsIterator extends AnyRef { ... }
trait RichIterator extends AbsIterator { ... }
class StringIterator extends AbsIterator { ... }
class Iter extends StringIterator with RichIterator { ... }

Iter の線形化は以下の通り。

{Iter, RichIterator, StringIterator, AbsIterator, ScalaObject, AnyRef, Any}

scala.ScalaObject は最後のミックスインとして加えられている。

class Iter extends StringIterator with RichIterator with ScalaObject

線形化は継承関係を精緻化するので Sub が Super のサブクラスのとき Sub と Super 両方が現れる線形化の中で Sub は Super よりも先に(左側に)現れる

abstract class AbsIterator extends AnyRef { ... }
class StringIterator extends AbsIterator { ... }

{ StringIterator, AbsIterator, ScalaObject, AnyRef, Any }
5.1.3 クラスメンバ(Class Members)
  • テンプレート C1 with ... with Cn { stats } によって定義されたクラス C
  • 文並び stats の中にメンバーを定義でき、全ての親クラスからメンバーを継承できる
  • メソッドの静的なオーバーロードは JavaC# と同様、同じ名前の複数のメソッドを定義・継承できる


メンバーマッチング

同じメンバー定義かどうかの判定。マッチする場合、サブ型では override でなければならない。

以下のいずれかに該当するかを判定する。その定義は 定義 5.1.4 参照。

  • 定義されたメンバーがどの親クラスのメンバーを override するか
  • C 中のオーバーロードされた変位指定として 2 つが共存するか


定義 5.1.4

メンバー定義 M とメンバー定義 M' が同じ名前を束縛し、次のいずれかが満たされるなら M は M' とマッチする。

  • M と M' のいずれもメソッド定義でない
  • M と M' ともに等価な引数型をもつ単相的なメソッド定義
  • M はパラメータなしのメソッド定義、 M' は空の引数リスト()のメソッド定義(あるいはその逆)
  • M と M' ともに同数の引数型 T、T' と同数の型パラメータ t、t' をもつ多相的メソッド定義


メンバー定義は以下のように分類できる。

  • 具象 or 抽象
  • 直接定義(C の文並び stats に現れる) or 継承されたもの


定義 5.1.5

  • クラス C の具象メンバー

あるクラス Ci ∈ LL(C) 中の全ての具象定義 M
先行するクラス Cj が M にマッチする具象メンバー M' を直接定義している場合を除く、Cj の定義は Cj ∈ LL(C) (j < i)

  • クラス C の抽象メンバー

あるクラス Ci ∈ LL(C) 中の全ての抽象定義 M
C が M にマッチするような具象メンバー M' を直接定義している場合を除く
先行するクラス Cj が M にマッチする抽象メンバー M' を直接定義している場合を除く、Cj の定義は Cj ∈ LL(C) (j < i)


この定義により、クラス C がマッチするメンバーとその親の間のオーバーライド関係(5.1.4)も決定される。

  • 具象定義は常に抽象定義をオーバーライドする
  • 共に具象 or 共に抽象な定義 M と M' について、もし M が M' を定義するクラスよりも(Cの線形化で)先行するクラス中に現れるなら M は M' をオーバーライドする


エラー条件

  • テンプレートが 2 つのマッチするメンバーを直接定義している
  • テンプレートが同じ名前と同じ消去型(erased type, 3.7)を含む
  • テンプレートが同じ名前で共にデフォルト引数を定義する(直接定義 or 継承されたもの)2 つのメソッドを含む


Example 5.1.6

トレイト D は抽象メンバー: h、g 具象メンバー: f をもつ。

trait  A  { def f: Int }
trait  B  extends A { def f: Int = 1 ; def g: Int = 2 ; def h: Int = 3 }
trait  C  extends A { override def f: Int = 4 ; def g: Int }
trait  D  extends B with C { def h: Int }
5.1.4 オーバーライド(Overriding)
  • 基底クラスの非 private なメンバー M' にマッチするメンバー M はそのメンバーをオーバーライドする
  • メンバー M の束縛はオーバーライドされるメンバー M' の束縛を包含しなくてはならない(3.5.2)

さらに以下のような制限がある

  • M' は final でないこと
  • M は private でないこと
  • M が取り囲むclass/package C について private[C] である場合 M' も private[C'] であり、かつ C' は C と等価か包含されていること
  • M が protected である場合 M' もまた protected であること
  • M' が抽象メンバーでない場合 M は override 修飾子が指定され、かつ以下のいずれかを満たすこと
    • M は M' が定義されたクラスのサブクラス内で定義されていること
    • M と M' 共に両クラスの(一つの)基底クラス内で定義された第 3 のメンバー M'' をオーバーライドしていること
  • M' が不完全(incomplete, 5.2)の場合 M は abstract override が指定されていること
  • M と M' ともに具象な値定義の場合、以下のいずれかを満たすこと
    • どちらも lazy 指定されていないこと
    • 両方とも lazy 指定されていること


Example 5.1.7

以下の型 C の定義は「type T

trait Root { type T <: Root }
trait A extends Root { type T <: A }
trait B extends Root { type T <: B }
trait C extends A with B

型 T の定義をオーバーライドすることでこの問題を解決できる。

class C extends A with B { type T <: C }


パラメータなしのメソッドについての特別なルール

空の引数リストを持つメソッドをオーバーライドした場合、オーバーライドしたメソッドも(定義時に指定しなかったとしても)引数リストを持つことが想定される(assumed)。

scala> class A { def say() = println('Hello) }
defined class A

scala> class B extends A { override def say = println('World) }
defined class B

scala> new B say
'World

scala> new B say()
'World

scala> class C { def say = println('!) }
defined class C

scala> new C say()
<console>:9: error: Unit does not take parameters
              new C say()
                    ^


上限境界に volatile 型を持つ抽象メンバーに関する制限

抽象型メンバーはその上限境界として volatile 型(3.6)をもつ場合 volatile 型の上限境界を持たない抽象メンバーをオーバーライドできない


デフォルト引数の継承

  • オーバーライドするメソッドはスーパークラス中の定義から全てのデフォルト引数を継承する
  • スーバークラスでデフォルト値が指定されていない場合に新しくデフォルト値を追加したり、オーバーライドすることもできる
5.1.5 継承クロージャ(Inheritance Closure)

継承クロージャ(Inheritance Closure)は以下のような方の最小の集合 SS

  • T が SS 中にあれば T の一部を構文的に形成する全ての型 T' もまた SS 中にある
  • T が SS 中のクラス型であれば T の全ての親は同じく SS 中にある

この語はこれ以降で一度も出現しないようだが・・?

5.1.6 事前定義(Early Definitions)
EarlyDefs              ::= '{' [EarlyDef {semi EarlyDef}] '}' 'with'
EarlyDef               ::= {Annotation} {Modifier} PatVarDef

事前フィールド定義(early field definition)節

テンプレートを以下のような形の事前フィールド定義節ではじめることができる。

{ val p1: T1 = e1
  ...
  val pn: Tn = en
} with sc with mt1 with ... with mtn { stats }

それによってスーパー型のコンストラクタ呼び出しの前にフィールド値を定義できる。

事前定義は少なくとも一つの変数を定義していなければならない。

事前フィールド定義節には、具象なフィールド定義以外を書くことはできない。


Example 5.1.8

scala> trait Greeting {
     |   val name: String // abstract
     |   val msg = "How are you, " + name
     | }
defined trait Greeting

scala> class BobGreeting extends { val name = "Bob" } with Greeting { println(msg) }
defined class BobGreeting

scala> val bg = new BobGreeting
How are you, Bob
bg: BobGreeting = BobGreeting@2c905b34

5.2 修飾子(Modifiers)

Modifier            ::=   LocalModifier
                      | AccessModifier
| 'override'
LocalModifier       ::=   'abstract'
                      |   'final'
                      |   'sealed'
                      |   'implicit'
                      |   'lazy'
AccessModifier      ::=   ('private' | 'protected') [AccessQualifier]
AccessQualifier     ::=   '[' (id | 'this') ']'

メンバー定義に修飾子が先行する場合があり、それはその識別子のアクセス性や使用法に影響を与える。

  • 同じ修飾子は一度しか現れてはならない
scala> private private val a = "a"
<console>:1: error: repeated modifier
       private private val a = "a"
               ^
  • 定義の繰り返しに先行する修飾子は、各定義すべてに適用される
scala> private val (a,b) = (1,2)

scala> object Example {
     |   val (a,b) = (1,2)
     | }
defined module Example

scala> Example.a
res7: Int = 1

scala> object Example2 {
     |   private val (a,b) = (1,2)
     | }
defined module Example2

scala> Example2.a
<console>:15: error: value a in object Example2 cannot be accessed in object Example2
              Example2.a
                       ^
private 修飾子
  • private 修飾子はテンプレート中の任意の定義・宣言で指定できる
  • private なメンバーは直接取り囲むテンプレートとそのコンパニオンモジュール、コンパニオンクラス(5.4)中からのみアクセス可能
  • サブクラスには継承されない、override もできない
修飾子の限定修飾(qualified)

例えば private[C] のようなものを考える。

  • この C は定義を囲む package か class を表していなければならない
  • この修飾子が指定されている場合、パッケージ C 中 or クラス C とそのコンパニオンモジュール(5.4)中からのみアクセス可能
  • C 中のテンプレートからのみ継承される

private[this] はまた異なる限定修飾で、それが定義されたオブジェクト(インスタンス)からのみアクセス可能。

非公開(private)
  • 限定修飾がない private なメンバーを「クラス非公開(class-private)」と呼ぶ
  • private[this] なメンバーを「オブジェクト非公開(object-private)」と呼ぶ
  • これらは抽象であってはならない
  • これらは protected や override を指定できない
限定非公開(qualified private)
  • private[C] (C は識別子)のように公開範囲を指定しているものを非公開に対して限定非公開と呼ぶ
protected

以下からアクセス可能。

  • 定義しているクラスのテンプレート
  • 定義しているクラスを基底クラスとして持つ、全てのテンプレート
  • それらクラスの任意のコンパニオンモジュール

protected[C] や protected[this] も指定できる。概要は private と同様。

package com.example {
  class X { protected[example] val value = "xxx" }
  package sub {
    object Main extends App {
      val x = new X
      println(x.value)
    }
  }
}

object Main2 extends App {
  import com.example.X
  val x = new X
  println(x.value)
}
/*
sample.scala:21: error: value value in class X cannot be accessed in com.example.X
 Access to protected value value not permitted because
 enclosing class object Main2 is not a subclass of 
 class X in package example where target is defined
  println(x.value)
            ^
*/
override
  • クラスメンバ宣言・定義に適用される
  • 親クラスのある具象メンバー定義を override するメンバー宣言・定義には必須
scala> trait Foo { def name: String }
defined trait Foo

scala> trait Bar extends Foo { def name: String = "bar" }
defined trait Bar

scala> trait Baz extends Bar { def name: String = "baz" }
<console>:9: error: overriding method name in trait Bar of type => String;
 method name needs `override' modifier
       trait Baz extends Bar { def name: String = "baz" }
                                   ^

scala> trait Baz extends Bar { override def name: String = "baz" }
defined trait Baz
abstract
  • trait の定義では不要、抽象な class の定義には必須
scala> abstract  class Foo { def foo: Unit }
defined class Foo

scala> abstract  class Foo { abstract def foo: Unit }
<console>:7: error: `abstract' modifier can be used only for classes;
it should be omitted for abstract members
       abstract  class Foo { abstract def foo: Unit }
                                          ^

scala> class Foo { abstract def foo: Unit }
<console>:7: error: `abstract' modifier can be used only for classes;
it should be omitted for abstract members
       class Foo { abstract def foo: Unit }
                                ^
abstract override
  • override abstract の組み合わせは trait の値メンバーにのみ許される
scala> trait Logger { def info(v: => String): Unit }
defined trait Logger

scala> trait MyLogger extends Logger { abstract override def info(v: => String): Unit = { println("***"); super.info(v); println("***") } }
defined trait MyLogger

scala> class LoggerFacade extends Logger { def info(v: => String) = println(v) }
defined class LoggerFacade

scala> val log = new LoggerFacade
log: LoggerFacade = LoggerFacade@1aa7aac

scala> log.info("foo")
foo

scala> val log = new LoggerFacade with MyLogger
log: LoggerFacade with MyLogger = $anon$1@884142

scala> log.info("foo")
***
foo
***

参考:
http://www.ne.jp/asahi/hishidama/home/tech/scala/trait.html

final
  • class 定義と class のメンバー定義で指定する(宣言にはつけられない)
  • abstract メンバーには指定できない
  • sealed とは一緒に使えない
scala> final abstract class Foo
<console>:7: error: illegal combination of modifiers: abstract and final for: class Foo
       final abstract class Foo
                            ^

scala> final trait Foo
<console>:7: error: illegal combination of modifiers: abstract and final for: trait Foo
       final trait Foo
                   ^

scala> final sealed class Foo
<console>:7: error: illegal combination of modifiers: final and sealed for: class Foo
       final sealed class Foo
                          ^

scala> final class Foo
defined class Foo

scala> class Foo { final def p(v: Any): Unit }
<console>:7: error: abstract member may not have final modifier
       class Foo { final def p(v: Any): Unit }
                             ^

scala> class Foo { final def p(v: Any): Unit = println(v) }
defined class Foo
sealed
  • sealed を指定された class/trait は同じソースファイル中で定義する場合を除き、直接には継承できない
  • sealed な class/trait のサブ型はどこからでも継承できる
lazy val
  • lazy val はそれが最初にアクセスされるときに初期化される(遅延評価)
scala> lazy var foo = "xxx"
<console>:1: error: lazy not allowed here. Only vals can be lazy
       lazy var foo = "xxx"
            ^

scala> lazy def foo: String = "xxx"
<console>:1: error: lazy not allowed here. Only vals can be lazy
       lazy def foo: String = "xxx"
            ^

scala> lazy val foo = "xxx"
foo: java.lang.String = <lazy>
  • lazy val の初期化中にアクセスしようとすると動作がループする場合があるので注意
lazy val x: Int = y
lazy val y: Int = x
println(x)

/*
java.lang.StackOverflowError
    at Main$$anon$1.x(loop.scala:1)
    at Main$$anon$1.y(loop.scala:2)
    at Main$$anon$1.x(loop.scala:1)
    at Main$$anon$1.y(loop.scala:2)
    at Main$$anon$1.x(loop.scala:1)
    at Main$$anon$1.y(loop.scala:2)
    at Main$$anon$1.x(loop.scala:1)
 ...
 */
  • lazy val の初期化中に例外が発生したら初期化していないとみなされ、後のアクセスで右辺の評価が再度試みられる

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]

型パラメータ節を持つ場合、そのクラスは「多相的(polymorphic)」であり、そうでない場合は「単相的(monomorphic)」となる。

最も一般的なクラス定義の形
class c[tps] as m(ps1)...(psn) extends t 
(n >= 0)
  • as:アノテーションの並び(なしでも可)
  • m:アクセス修飾子(private/protected とか)

例えば継承先からコンストラクタがみえるかどうかはそのスコープによって決まる。

class Protected protected(a:Int)
class ExtProtected(a:Int) extends Protected(a)

class Private private(a:Int)
class ExtPrivate(a:Int) extends Private(a)

Private のコンストラクタはそのサブ型からは見えない

example.scala:5: error: constructor Private in class Private cannot be accessed in class ExtPrivate
class ExtPrivate(a:Int) extends Private(a)
                ^
one error found

private はこういう使い方?

class Private2 private(a:Int)
object Private2 {
  def create(a:Int) = new Private2(a)
}
  • (ps1)...(psn):基本コンストラクタの値パラメータ節

値パラメータに val キーワードが先行するなら getter が var キーワードが先行するなら getter/setter 定義が暗黙に加えられる。

  • t:テンプレート

5.1 のおさらい。テンプレートは以下のような形式。

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

sc:テンプレートのスーパークラスを定義するコンストラクタ呼び出し
mt1,...,mtn (n >= 0):テンプレートのトレイトを定義するトレイト参照
stats:テンプレートの初期化コードと追加のメンバー定義を含む文の並び。

テンプレートをそれなりに使った例だと以下のような感じ?

scala> abstract class Base(x: Int, y: String)
defined class Base

scala> class C[A] private(x: Int, val y: String, var z: List[String]) extends Base(x,y) with Serializable { }
defined class C
5.3.1 コンストラクタ定義(Constructor Definitions)
FunDef         ::= 'this' ParamClause ParamClauses
                   ('=' ConstrExpr | [nl] ConstrBlock)
ConstrExpr     ::= SelfInvocation
                 | ConstrBlock
ConstrBlock    ::= '{' SelfInvocation {semi BlockStat} '}'
SelfInvocation ::= 'this' ArgumentExprs {ArgumentExprs}

クラス定義時に値パラメータ節を受け取って定義される基本コンストラクタ(primary constructor)以外にコンストラクタ(補助コンストラクタ)を定義したい場合は以下のような形で定義する。基本コンストラクタ同様にカリー化された引数リストを使うことができる。

def this(ps1)...(psn) = e
  • (ps1)...(psn):値パラメータ節
  • e:コンストラクタ式

補助コンストラクタは、最終的に基本コンストラクタを呼び出す形にしないとダメ。

scala> class Foo { def this(x:String) = { this() } }
defined class Foo

scala> class Foo { def this(x:String) = this(); def this(x:String, y:Int) = this(x) }
defined class Foo

scala> class Foo { def this(x:String) = {} }
<console>:1: error: 'this' expected but '}' found.
       class Foo { def this(x:String) = {} }
                                         ^
5.3.2 ケースクラス(Case Classes)
TmplDef ::=     'case' 'class' ClassDef

クラス定義に先行して case が置かれているとそのクラスはケースクラスとなる。

ケースクラスの基本コンストラクタに渡される値パラメータは「要素(elements)」と呼ばれる。

パラメータに val/var が指定されていなければ暗黙に val が加えられる。

c[tps](ps1)...(psn) 

のような定義のケースクラスには以下のようなコンパニオンオブジェクトが自動的に生成される。

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)
}
class c {
  // 定義された内容に追加で copy というメソッドが追加される
  def copy[tps](ps´1)...(ps´n): c[tps] = new c[Ts](xs1)...(xsn)
  // 以下のメソッドは override される
  override def equals:(Any)Boolean = ...
  override def hashCode: Int = ...
  override def toString:String = ...
}
5.3.3 トレイト(Traits)
TmplDef          ::= 'trait' TraitDef
TraitDef         ::= id [TypeParamClause] TraitTemplateOpt
TraitTemplateOpt ::= 'extends' TraitTemplate | [['extends'] TemplateBody]
  • ミックスインとして他のクラスに加えられることを意図したクラス
  • コンストラクタパラメータをもつことができない
scala> trait Foo(x:Int,y:Int)
<console>:1: error: traits or objects may not have parameters
       trait Foo(x:Int,y:Int)
                ^

実際にはトレイトは全てスーパークラスが初期化された後で初期化されるのでそもそも不要。

scala> abstract class Base(x:String,y:Int)
defined class Base

scala> trait Foo extends Base
defined trait Foo

scala> trait Foo extends Base("aaa",123)
<console>:1: error: parents of traits may not have parameters
       trait Foo extends Base("aaa",123)
                             ^

scala> class Foo extends Base("aaa",123)
defined class Foo

TODO: actual supertype と最小固有のスーパー型について

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

ObjectDef      ::=   id ClassTemplat

オブジェクト定義は、新しいクラスのただ 1 つのオブジェクトを定義する。

object m extends t

t:テンプレート(sc with mt1 with ... with mtn {stats})

テンプレート t に適合するただ 1 つのオブジェクト(モジュール)を定義する。以下のような遅延評価値定義とほぼ同じ。

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

コンパニオンモジュール

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