Scalaの特徴を紹介し、基本構文や関数、クラスなど、Scalaの基本的な機能について解説する入門連載
前回の記事「JavaのGenericsよりも便利なScalaの型パラメータ」では、型パラメータによるジェネリクス(総称型)プログラミング手法について紹介しました。型パラメータを使用すると、より柔軟性のあるプログラミングが可能となり、さまざまな場面で役立ちます。
今回は前回の型パラメータと並ぶ機能である抽象型と、Scalaをより便利に使用するための機能、暗黙の型変換/暗黙のパラメータを紹介します。
第1回記事では、Scala標準のREPLとScala IDEで動作を確認してみました。今後本記事のサンプルコードは、どちらで確認しても問題はありませんが、対話的に実行でき、1文ごとにコードの結果が分かって便利なので、基本的にはREPLを用いて説明していきます。
Scala IDEを使用する場合、第1回記事の『Scala IDE for Eclipseで「Hello Scala!」』を参照してプロジェクトを作成して実行してください。REPLを使用する場合は、コンソール上でscalaコマンドを実行し、REPLを起動してください。
オブジェクト指向言語を使用するとき、抽象クラスや抽象メソッドは「継承」によって具体的な実装を与えられることを期待しています。Scalaではクラスやメソッド以外に、フィールドや型までも抽象的に宣言できます。
前回は型パラメータを用いて、型自体をクラスやメソッドのパラメータとして受け取る手法を紹介しました。型パラメータはオブジェクト指向言語において一般的な機能ですが、Scalaは「抽象型」と呼ばれる型も使えます。
「抽象型」とは、クラスやトレイト内で型を指定していないメンバで、「type」キーワードによってクラス/トレイトのメンバを宣言し、そのクラス/トレイトを継承/ミックスインしたクラスで具体的な型を指定します。
まずは抽象型を使用してみましょう。REPLを起動し、抽象型を宣言した「Base」という抽象クラスを宣言します。
abstract class Base { type SomethingFoo def show(something:SomethingFoo) }
抽象クラスBaseでは、「type SomethingFoo」という宣言をしています。これが抽象型の宣言に当たり、「SomethingFoo」という名前の抽象型の宣言です。これが実際にどのような型なのかは、この時点では分かりませんが、その型を引数に取る「show」メソッドを持っています。
次に、抽象型に指定するFooクラスと、Baseクラスを継承したEx1クラスを宣言しています。Ex1クラスでは、先ほどのSomethingFooにFooクラスを指定しています。
これにより、先ほど宣言したSomethingFoo抽象型に具体的なFooという型が指定されます。showメソッドでは、SomethingFoo(Foo)のexecメソッドを呼び出すように実装しています。
class Foo { def exec = println("Foo#execを実行") } class Ex1 extends Base { type SomethingFoo = Foo def show(something:SomethingFoo) = something.exec }
最後に、Ex1クラスをインスタンス化して使用します。showメソッドにFoo型を渡しているのが分かります。
scala> val x = new Ex1 x: Ex1 = Ex1@6045f029 scala> x.show(new Foo) Foo#execを実行
なお、抽象型でも型パラメータのときと同じく、型境界を指定できます。下記例では、抽象型SomethingFooに指定できる型を、FooクラスかFooを継承したクラスに限定しています。
abstract class Base { type SomethingFoo <: Foo …… }
また、typeのもう1つの使い方としては、型名が長すぎてコードが見づらいときに別名を付けるような用途もあります。typeは型パラメータも含んだ状態で別名を使えるので、下記のような使い方をすれば記述量を削減できます。
type X = List[(Int,String,Double)] def func(arg1:X,arg2:X):X = ・・・
次は、暗黙の型変換を紹介します。
暗黙の型変換(implicit conversion)とは、ある型から別の型への変換を事前に変換用関数を用意して自動で行う機能です。例えば、下記例ではString型の変数にInt型の値を代入しようとしていますが、型が違うので当然エラーになります。
scala> val str:String = 10 <console>:7: error: type mismatch; found : Int(10) required: String val str:String = 10 ^
ではここで、暗黙の型変換を行う関数を定義してみましょう。暗黙の型変換を行う関数は「implicit」キーワードを付与し、引数に変換元の型、戻り値に変換先の型を指定します。
implicit def intToString(num:Int):String = { println("数値から文字列へ変換") num.toString }
もう一度String型の変数にInt型の値を代入してみます。
scala> val str:String = 10 数値から文字列へ変換 str: String = 10
今度は暗黙の型変換を行う関数が呼ばれ、代入が成功します。それでは、この動作について説明しましょう。
implicitキーワードが付与された関数は、型のチェックエラーを修正するためにコンパイラがプログラムに挿入する関数です。ソース上で「a + b」という式が型のチェックでエラーになってコンパイルできないとき、「暗黙の型変換関数(a) + b」という処理に変換してコンパイルしようとします。
暗黙の型変換関数がaを、+メソッド持つ型に変換できれば、そのまま動作します。暗黙の型変換を行う関数がシンプルな変換関数であり、その記述を省略できるとすると、ソースファイルはとても簡潔になります。
なお、関数名は何でも良いのですが、{変換元}To{変換先}という名前にすることが多いようです。
もう1つ、Date型からString型へ暗黙の型変換を行う関数を定義して使用してみましょう。SimpleDateFormatを使用してDate型を固定フォーマットの文字列へ変換しています。
implicit def dateToString(date:java.util.Date):String = { import java.text._ println("java.util.DateからStringへ変換") val sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss") sdf.format(date) } scala> val strDate:String = new java.util.Date() java.util.DateからStringへ変換 strDate: String = 2012/06/17 10:10:05
Copyright © ITmedia, Inc. All Rights Reserved.