Scala言語仕様 2.8 リーディング - 5 クラスとオブジェクト
Scala言語仕様
日本語訳(Scala 2.8)
http://www.scala-lang.org/docu/files/LangSpec2.8-ja_JP.pdf
原文(Scala 2.9)
http://www.scala-lang.org/docu/files/ScalaReference.pdf
目次
- 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 のテンプレートでない場合
以下の順序で評価される。
暗黙に 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 の中にメンバーを定義でき、全ての親クラスからメンバーを継承できる
- メソッドの静的なオーバーロードは Java や C# と同様、同じ名前の複数のメソッドを定義・継承できる
メンバーマッチング
同じメンバー定義かどうかの判定。マッチする場合、サブ型では 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 }
コンパニオンモジュール
クラスと同じ名前をもつオブジェクトであり、同じスコープとコンパイル単位で定義される。
逆に、そのクラスはモジュールのコンパニオンクラスと呼ばれる。