Class,Abstract Class,Trait,Objectの違いまとめ
ScalaByExample - Example 6 Classes and Objectsより。ScalaのClassとかTraitの違いについて、わかる範囲でまとめてみます。
ScalaのClassとかTraitの比較表
Class | Abstract Class | Trait | Object | |
---|---|---|---|---|
抽象メソッドを持てるか? | × | ○ | ○ | × |
メソッドの実装を書けるか? | ○ | ○ | ○ | ○ |
インスタンス化が可能か? | ○ | × | × | ○(ただしインスタンスは一つだけ) |
コンストラクタ引数の指定ができるか? | ○ | ○ | × | × |
継承の制約 | Class,Abstract Classのうち1つと、複数のTraitを継承できる | 同左 | 同左 | 同左 |
比較用:JavaのClassとかInterfaceの比較表
JavaのClassとかInterfaceを同じ表に当てはめるとこんな感じになります。
Class | Abstract Class | Interface | |
---|---|---|---|
抽象メソッドを持てるか? | × | ○ | ○ |
メソッドの実装を書けるか? | ○ | ○ | × |
インスタンス化が可能か? | ○ | × | × |
コンストラクタ引数の指定ができるか? | ○ | ○ | × |
継承の制約 | Class,Abstract Classのうち1つと、複数のInterfaceを継承(実装)できる | 同左 | 複数のInterfaceを継承できる |
- Class,Abstract ClassはJavaと同じ。使い道も同じ(たぶん)
- TraitはInterfaceに近いけど、
- ObjectはScala独自のものだけど、Javaのstaticな定数に近いかな。(定数という意味ではEnum(のインスタンス)にも近いかも)
// ↓この定数がObjectに近い static Foo FOO = new Foo() { void foo() {} }
この条件だと、任意のクラスを多重継承できるんでは?
Traitの継承制約が思ってたより緩くて意外。この制約だとTraitを挟むことで任意のクラスを多重継承できそう!に見えるんだけど、やっぱりできません。
// クラス class A { def a = "a" } class B { def b = "b" } // クラスを派生するTrait trait X extends A {} trait Y extends B {} // TraitをMixinしたクラス class Foo extends X with Y {}
これをコンパイルすると、次の通りエラーになります。
ClassSample.scala:12: error: illegal inheritance; superclass A is not a subclass of the superclass B of the mixin trait Y class Foo extends X with Y {} ^ one error found
A,Bが派生関係にあって、「extends X with Y」の順で派生していればOK。
// クラス class A { def a = "a" } class B extends A { def b = "b" } // クラスを派生するTrait trait X extends A {} trait Y extends B {} // TraitをMixinしたクラス class Foo extends Y with X {}
このとき、FooはBの派生クラスという扱いになるらしい。つまり、内部的には、
class Foo extends B with Y with X {}
と補完されており、暗黙的にFooはB派生と見なされる。ここでBがAを継承していればつじつまが合うのでOKだが、継承していなければFooはAかつB派生にはできないのでエラーになる。ということで、Classを継承したTraitは、Traitを継承したクラスが、Traitの親クラスを継承することを強制したい時に使う機能っぽい。