クラスの継承

単一継承

Scala のクラスの継承は単一継承のようだ。もう少し言うと、スーパークラスのほかにトレイトというものを複数継承できるようだけど、トレイトについては日を改める。

クラスを継承するためには、extends キーワードに続けてスーパークラス名をつければいい。例を示そう。

scala> class Foo() {
     |     def foo(): Unit = println("Foo")
     | }
defined class Foo

scala> class Bar() extends Foo {
     |     def bar(): Unit = println("Bar")
     | }
defined class Bar

ここではクラス Bar がクラス Foo を継承している。Foo にはメソッド foo があり、Bar には Foo から継承したメソッド foo と Bar で定義されたメソッド bar がある。

scala> val foo = new Foo()
foo: Foo = Foo@3e81c10b

scala> foo.foo
Foo

scala> val bar = new Bar()
bar: Bar = Bar@5d842ce8

scala> bar.foo
Foo

scala> bar.bar
Bar

override

継承したクラス(サブクラス)で、スーパークラスのメソッドをオーバーライドしたいときには、明示的に override キーワードをつける必要がある。これによって、オーバライドするつもりで新しいメソッドを定義してしまったり、逆に新しいメソッドを定義するつもりでオーバーライドしてしまうようなことを防いでいるようだ。

scala> class Baz() extends Foo {
     |     override def foo(): Unit = println("Baz")
     | }
defined class Baz

scala> val baz = new Baz()
baz: Baz = Baz@7b6dd92d

scala> baz.foo
Baz

この例では、Baz でメソッド foo をオーバーライドしている。

ところで、何気なく書いたけど、引数のないメソッドの呼び出しはカッコを省略できるんだな。

複数の引数リストを持つメソッドと部分適用

Scala では複数の引数リストを持つメソッドを定義できる。読んで字の如く、引数リストが複数あるってことだ。

scala> class Adder {
     |     def add(x: Int)(y: Int) = x + y
     | }
defined class Adder

こんなふうに、引数リストを囲むカッコを連ねる。呼び出すときも同様。

scala> val adder = new Adder()
adder: Adder = Adder@2a83bab5

scala> adder.add(2)(3)
res0: Int = 5

引数リストの代わりに _ を使うと部分適用ができる。

scala> val fun = adder.add(3) _
fun: Int => Int = $$Lambda$3397/284877983@4dddc7e7

この fun は関数オブジェクトのようなものなのかな。残りと引数を与えると値が返ってくる。

scala> fun(4)
res1: Int = 7

いまは後ろの引数リストの代わりに _ を使ったけど、前の引数リストの代わりには使えないんだろうか。

scala> val fun2 = adder.add _ (3)
<console>:1: error: ';' expected but '(' found.
       val fun2 = adder.add _ (3)
                              ^

ダメか。

部分適用は、単一の引数リストでも使える。Adder クラスを定義しなおしてみよう。

scala> class Adder {
     |     def add(x: Int, y: Int) = x + y
     | }
defined class Adder

この新しい Adder クラスの add メソッドで部分適用を試してみる。

scala> val fun: Int => Int = adder.add(2, _)
fun: Int => Int = $$Lambda$3365/582762225@4e9e818e

scala> fun(3)
res0: Int = 5

関数オブジェクト(?)の型はを省略できないみたいだ。省略するとエラーになる。

scala> val fun = adder.add(2, _)
<console>:12: error: missing parameter type for expanded function ((x$1: ) => adder.add(2, x$1))
       val fun = adder.add(2, _)
                              ^

ところで、こっちの形式なら後ろの引数を部分適用できるんだろうか。

scala> val fun2: Int => Int = adder.add(_, 3)
fun2: Int => Int = $$Lambda$3381/567903408@64679683

scala> fun2(5)
res1: Int = 8

できた。