Scalaのパッケージ、アクセス修飾子、オブジェクト継承:スケーラブルで関数型でオブジェクト指向なScala入門(6)(3/3 ページ)
Scalaの特徴を紹介し、基本構文や関数、クラスなど、Scalaの基本的な機能について解説する入門連載
スーパークラスのコンストラクタにアクセス
前述のEngineerクラスはコンストラクタを持っていませんでしたが、コンストラクタを持っていた場合、サブクラスから呼び出したい場合もあります。そのような場合、サブクラス定義時に呼び出したいスーパークラスのコンストラクタを指定できます。
ではEngineerクラスにnameを受け取るコンストラクタを追加して、サブクラスであるProgrammerクラスから呼び出してみましょう。
abstract class Engineer(val name:String) { println("Engineer.name=" + name) def work():Unit } class Programmer(name:String,val age:Int) extends Engineer(name){ println("Programmer.name=" + name) println("Programmer.age=" + age) def work() = printf("%s(%d)さんはコーディングします",name,age) }
サブクラスからスーパークラスのコンストラクタを呼び出すには、extendsでクラスを指定した後にコンストラクタの引数を渡します。ここで指定する引数は、スーパークラスの基本コンストラクタのものでも補助コンストラクタのものでも構いません。
Programmerクラスはコンストラクタで「name」「age」を受け取り、nameをスーパークラスのコンストラクタへ渡しています。実際にREPLでProgrammerクラスをインスタンス化してみます。
scala> val p = new Programmer("taro",30) Engineer.name=taro Engineer.age=30 Programmer.name=taro Programmer.age=30 p: Programmer = Programmer@2bbf1be2
最初にスーパークラスのコンストラクタが呼ばれ、その後にサブクラスのコンストラクタが実行されています。
「final」で継承やオーバーライドの禁止
サブクラスでメンバをオーバーライドさせたくないときには、「final」キーワードを使います。クラスのメンバに「final」を付ければオーバーライドを禁止できますし、クラスに「final」を付ければ継承自体を禁止できます。
//finalクラス final class X { def print() = println("final class") } class Y { //finalメソッド final def print() = println("final method") } //エラー class EX_X extends X class EX_Y extends Y { //エラー override def print() = println("override method") }
「シールドクラス」でサブクラスの制限
シールドクラスとは、サブクラスを作成するための制限を付けたクラスです。
シールドクラスを定義するには、クラス定義の先頭に「sealed」キーワードを付けます。sealedを付けられたクラスは、「同一ファイル内」のクラスからは継承できますが、別ファイル内で定義されたクラスからは継承できないという特徴を持ちます。
ただし、シールドクラスを継承したクラスは、別ファイルのクラスからも継承可能です。
シールドクラスはパターンマッチを記述するとき役に立ちます。ケースクラスを使用してパターンマッチを記述するとき、可能なケースを網羅しなければいけません。最後にデフォルト式を追加すれば漏れはなくなりますが、デフォルト処理がないときはすべてのケースが網羅されたという保証がなくなっていまいます。
シールドクラスを継承するケースクラスを使ってマッチ式を書くと、対応できていないパターンの組み合わせをチェックして警告してくれます。
シールドクラスの使い方
例を見てみましょう。Engineerクラスをシールドクラスとして、それを継承するクラスを複数作成します(※これらは1つのソースファイルに記述してください)。
package sealedmodel sealed abstract class Engineer case class Programmer extends Engineer case class Tester extends Engineer case class Architect extends Engineer
ここで、わざとパターンマッチに漏れがある記述をしてみます。
import sealedmodel._ object Main { def main(args: Array[String]) = { val e:Engineer = new Programmer e match { case p:Programmer => println("Programmer") case t:Tester => println("Tester") } } }
Architectに対するマッチ式も、デフォルト式も記述していません。そのため、下記のような警告が出ます。
match is not exhaustive! missing combination Architect
このように、sealedを使えばケース漏れで実行時にMatchErrorが発生するのを予防できます。
@uncheckedアノテーション
しかし時には、この警告が煩わしいこともあります。例えば、あるケースではProgrammerかTesterしかあり得ないということが分かり切っているときなどです。
そういったときには通常、デフォルト式(case _)を追加すればよいのですが、わざわざ実行されないことが分かっているコードを記述するのも面倒です。
そんなときは@uncheckedアノテーションを使用しましょう。
(e: @unchecked) match { case p:Programmer => println("Programmer") case t:Tester => println("Tester") }
式の後ろに「:」を付けて、@とアノテーションの名前を記述します。パターンマッチのセレクタ式が、この@uncheckedアノテーションを持つ場合、その後に記述される各パターンに対してチェックを行いません。
次回はScalaの重要な機能、トレイト
今回はパッケージとインポートのいろいろな使用方法を紹介しました。また、アクセス修飾子の種類や、Scalaにおけるオブジェクトの継承、抽象クラスやオーバーライドの基本、finalクラス、sealedクラスの使い方も紹介しました。
全体的にJavaよりも多様な機能があり、より細かく制御できるようになっています。
次回はScalaの重要な機能の1つ、トレイトを紹介します。
筆者紹介
中村修太(なかむら しゅうた)
クラスメソッド勤務の新しもの好きプログラマーです。昨年、東京から山口県に引っ越し、現在はノマドワーカーとして働いています。好きなJazzを聴きながらプログラミングするのが大好きです。
- カリー化、遅延評価などScalaの文法総まとめ&今後
- Scalaの並行処理とアクター、並列コレクション
- Scalaの抽象型と暗黙の型変換/引数、パラメータ制約
- JavaのGenericsよりも便利なScalaの型パラメータ
- Scalaのトレイトでプログラマをミックスインしてやんよ
- Scalaのパッケージ、アクセス修飾子、オブジェクト継承
- Scalaのクラスとオブジェクト、パターンマッチ
- 基本的なパターンマッチとScalaで重要な“関数”
- Scalaの基本的なコレクション4タイプと制御構文・例外
- Scalaプログラミングで知っておきたい基本構文まとめ
- EclipseでScalaプログラミングを始めるための基礎知識
Copyright © ITmedia, Inc. All Rights Reserved.