クラスに複数のコンストラクタを定義しなければならない場合もありますが、そのような場合、「補助コンストラクタ」を定義できます。補助コンストラクタの構文は、以下のようにします。
def this({任意の引数}) = {補助コンストラクタの処理}
補助コンストラクタの最初に、他のコンストラクタを呼び出す必要があります。「Programmer」クラスに引数なしの補助コンストラクタを定義してみましょう。以下の補助コンストラクタでは、最初に「Scala」という文字列を指定して基本コンストラクタを呼び出しています。
class Programmer(val language: String) { println("Programmerインスタンスを生成します") println("language = " + language) /** 補助コンストラクタ */ def this() = this("Scala") def coding() = println(language + "を使ってコーディングします") }
なお、ここでは「this」というキーワードを使用して補助コンストラクタを定義していますが、Javaと同じように、クラス内で自身に定義されたフィールドやメソッドを呼び出す際、それを明示するために「this」も使えます。
…… def coding() = println(this.language + "を使ってコーディングします") ……
いままでJavaを使用してきた人は、クラスに定義してあるフィールドやメソッドを見て、「アクセス修飾子の『public』は付けないのか?」と思った人がいるかもしれません。
Scalaでは、アクセス修飾子を付けないと、Javaでいう「public」(どこからでもアクセス可能)と同じ意味になります。
自分のクラスからのみアクセス可能としたい場合、フィールドやメソッドの先頭に「private」修飾子を付けます。
class Sample { val arg1 = "this is public " private val arg2 = "this is private " def func1() = "this is public function" private def func2() = "this is private function" }
Scalaでは、Javaよりも詳細なアクセス制御が可能です。アクセス修飾子については今後の連載で解説します。
コンストラクタやメソッド内で、クラスや関数の利用者に対して制約を課したいとき、「require」を使用して引数の検証ができます。これは、以下の構文を使用し、条件式満たさないときに例外を発生させます。
require({条件式})
コンストラクタの引数にnullが渡されたときにエラーが発生するようにしてみましょう。
class Programmer(_language: String) { println("Programmerインスタンスを生成します") println("language = " + _language) //引数がnullだったらエラー require(_language != null) val language = _language /** 補助コンストラクタ */ def this() = this("Scala") def coding() = println(language + "を使ってコーディングします") }
コンストラクタにnullを渡すと、IllegalArgumentExceptionが発生します。
同じメソッド名で異なる個数の引数、または異なる型の引数を取るメソッドを複数定義することを「オーバーロード」といいます。Javaでも用意されている機能ですが、Scalaでも同じように使えます。
class Sample { def add(x:Int, y:Int) = x + y def add(x:Double, y:Double) = x + y def add(x:Int, y:Int, z:Int) = x + y + z } defined class Sample scala> val s = new Sample s: Sample = Sample@81e4a1 scala> s.add(1,2) res34: Int = 3 scala> s.add(1D,2D) res35: Double = 3.0 scala> s.add(1,2,3) res36: Int = 6
渡された引数の型、数によって呼び出されるメソッドが切り替わります。
筆者は、Javaでユーティリティクラスを用意するとき、privateコンストラクタを定義してインスタンス化を禁止し、「static」メソッドを定義していました。
Scalaにはstaticがないため、そういった用途のクラスを作成したい場合には「シングルトンオブジェクト」を使用します。シングルトンオブジェクトとは、インスタンスがプログラム内に1つしか存在しないクラスのオブジェクトです。
Scalaでは、「class」の代わりに「object」を使用するだけで簡単にシングルトンオブジェクトを定義できます。
object SampleUtil{ def hello() = println("hello") } scala> SampleUtil.hello() hello
objectを使用した場合、定義時でなく初回アクセス時にインスタンスが自動的に生成されます。インスタンスは1つだけしか作成できないため、newは使えません。またコンストラクタは、引数を指定することや、補助コンストラクタを持つこともできません。
それ以外はclassを使用した場合とほぼ同じように使えます(※「抽象メンバ」今後の連載で紹介)は定義できません)。
「コンパニオンオブジェクト」とは、あるクラスに対して同じスコープ、同じ名前で定義されたシングルトンオブジェクトです。classとobjectが同じファイル、同じパッケージの中に、同じ名前で宣言されていれば、それはコンパニオンオブジェクトとなります。
コンパニオンオブジェクトは、コンパニオンクラスのprivateなフィールドやメソッドに対してアクセスができるという特徴があります。
下の例では、クラスのprivateなコンストラクタに対して、objectからアクセスしています(※クラス名の後にprivateを付けると、基本コンストラクタがprivateになります)。
class SampleCompanion private (num:Int) object SampleCompanion { def apply(num:Int) = { new SampleCompanion(num) } } object Main { def main(args: Array[String]) = { val ins = SampleCompanion(10) println(ins) } }
REPL上ではコンパニオンオブジェクトをそのまま入力して動かせません。
サンプルを動作させたい場合、REPL上で「:paste」を入力してから「SampleCompanion」クラスとオブジェクトを
コピー&ペーストするか、Eclipseで同一ファイル内にclassとobjectを定義し、main関数内で呼び出してください。
SampleCompanionオブジェクトには、「apply」というメソッドが定義されています。「apply」は連載第2回の「Scalaプログラミングで知っておきたい基本構文まとめ」で少しだけ紹介しましたが、「apply」という名前は特別な意味を持っており、オブジェクトのメソッドを関数を扱うように呼び出せます。
上記例でいうと、「SampleCompanion.apply(10)」と記述するところを、「SampleCompanion(10)」と記述し、「SampleCompanion」クラスのインスタンスを生成しています。「object」で「apply」メソッドが定義される場合、ファクトリ的な使い方をするケースが多いようです。
次ページでは、オブジェクトに対してパターンマッチを行う方法を紹介します。
Copyright © ITmedia, Inc. All Rights Reserved.