連載
» 2012年08月24日 00時00分 公開

スケーラブルで関数型でオブジェクト指向なScala入門(9):Scalaの抽象型と暗黙の型変換/引数、パラメータ制約 (1/3)

Scalaの特徴を紹介し、基本構文や関数、クラスなど、Scalaの基本的な機能について解説する入門連載

[中村修太,クラスメソッド株式会社]

前回のおさらいと今回の内容

 前回の記事「JavaのGenericsよりも便利なScalaの型パラメータ」では、型パラメータによるジェネリクス(総称型)プログラミング手法について紹介しました。型パラメータを使用すると、より柔軟性のあるプログラミングが可能となり、さまざまな場面で役立ちます。

 今回は前回の型パラメータと並ぶ機能である抽象型と、Scalaをより便利に使用するための機能、暗黙の型変換/暗黙のパラメータを紹介します。

 第1回記事では、Scala標準のREPLScala IDEで動作を確認してみました。今後本記事のサンプルコードは、どちらで確認しても問題はありませんが、対話的に実行でき、1文ごとにコードの結果が分かって便利なので、基本的にはREPLを用いて説明していきます。

 Scala IDEを使用する場合、第1回記事の『Scala IDE for Eclipseで「Hello Scala!」』を参照してプロジェクトを作成して実行してください。REPLを使用する場合は、コンソール上でscalaコマンドを実行し、REPLを起動してください。

Scalaの「抽象型」とは

 オブジェクト指向言語を使用するとき、抽象クラス抽象メソッドは「継承」によって具体的な実装を与えられることを期待しています。Scalaではクラスメソッド以外に、フィールドまでも抽象的に宣言できます。

 前回は型パラメータを用いて、型自体をクラスやメソッドのパラメータとして受け取る手法を紹介しました。型パラメータはオブジェクト指向言語において一般的な機能ですが、Scalaは「抽象型」と呼ばれる型も使えます。

 「抽象型」とは、クラスやトレイト内で型を指定していないメンバで、「type」キーワードによってクラス/トレイトのメンバを宣言し、そのクラス/トレイトを継承/ミックスインしたクラスで具体的な型を指定します。

「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」キーワードを付与し、引数に変換元の型、戻り値に変換先の型を指定します。

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{変換先}という名前にすることが多いようです。

Date型からString型へ暗黙の型変換

 もう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
       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。