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

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

[中村修太,クラスメソッド株式会社]
前のページへ 1|2|3       

型パラメータの制約

 型パラメータとimplicitに関連した機能をもう1つ紹介しましょう。暗黙の引数を利用すると、型パラメータが特定のクラスのときだけ呼び出せる制約を持ち、それをコンパイル時にチェックされるメソッドを作ることができます。これを「型パラメータの制約(Generalized Type Constraints)」といいます(※日本語での名称は特に決まっていません)。

 この型パラメータ制約はいくつか種類があるので、それぞれ紹介します。

「A =:= B」の場合

 まずは、「A =:= B」というパラメータ制約です。これはAの型がBの型と等しければメソッドを呼び出せるという制約です。

 下記サンプルをご確認ください。Xクラスは型パラメータを1つ受け取ります。受け取るパラメータは何を指定しても問題ありませんが、execメソッドを使用するためには、型パラメータがInt型でなければ使用できない制約になっています。

class X[A]{
    def exec(implicit t:A =:= Int):Unit = println("exec")
}

 暗黙のパラメータを使用し、「=:=(Predefクラスに定義してある)」で型パラメータがどの型なのかをチェックします。実際にXクラスを使用してみましょう。型パラメータにInt型とString型、それぞれ指定して動作を確認してみます。

scala> val x = new X[Int]
x: X[Int] = X@60813aca
        
scala> x.exec
exec
scala> val x2 = new X[String]
x2: X[String] = X@1e680525
scala> x2.exec
<console>:10: error: Cannot prove that String =:= Int.
              x2.exec
                 ^
		

 型パラメータにInt型を指定した場合、execメソッドも問題なく使用できています。型パラメータにString型を指定した場合、Xクラスのインスタンス化は問題ありませんが、execメソッドを使用したところでエラーになります。

「A <:< B」の場合

 次は「A <:< B」と指定する制約です。これは、「AはBと同じ、もしくはサブクラスである」という制約です。Baseクラスとそれを継承したEx1クラス、それらに関係ないAnotherクラスを用意します。Yクラスでは型パラメータの制約をBaseかそのサブクラスに制限しています。

class Base
class Ex1 extends Base
class Another
 
class Y[A] {
    def exec(implicit t:A <:< Base):Unit = println("exec")
}

 Yクラスをインスタンス化して使用してみます。型パラメータにEx1クラスとAnotherクラスを指定して、それぞれ確認してみましょう。

scala> val y = new Y[Ex1]
y: Y[Ex1] = Y@21b6484c
 
scala> y.exec
exec
 
scala> val y2 = new Y[Another]
y2: Y[Another] = Y@16030e71
 
scala> y2.exec
<console>:12: error: Cannot prove that Another <:< Base.
              y2.exec
                ^

 Baseクラスを継承するEx1クラスを型パラメータに指定した場合は問題なくexecメソッドを使用できています。Baseクラスと関係のないAnotherクラスを指定した場合は、execメソッドを使用したところでエラーになっています。

「A => B」の場合

 最後は「A => B」という制約です。これは先ほどご紹介したの可視境界と意味は同じで、「Aは暗黙の型変換でBに変換できる(AをBとして扱える)」という制約です(※Scala2.9までは「A <%< B」と記述していましたが、現在はdeprecatedになっています)。

 下記サンプルのZクラスでは、型パラメータの制約を「指定したクラスはYクラスとして扱えること」という制限しています。

class X
class Y
class Z[A]{
    def exec(implicit t:A => Y):Unit = println("exec")
}

 Xクラスを型パラメータに指定してZクラスをインスタンス化してみましょう。

scala> val z = new Z[X]
z: Z[X] = Z@2f1261b1
 
scala> z.exec
<console>:12: error: No implicit view available from X => Y.
              z.exec
                ^

 XクラスをYクラスに変換する暗黙の型変換関数はまだ用意していないので、execメソッドを使用しようとするとエラーになります。ではXクラスをYクラスに変換する暗黙の型変換関数を定義して、もう一度execメソッドを使用してみます。

implicit def xToy(x:X):Y = new Y
 
scala> z.exec
exec

 今度は普通にexecメソッドを使えました。上限/下限境界や可視境界では、型パラメータに指定する型を制限できましたが、型パラメータ制約(Generalized Type Constraints)では、型パラメータに指定する型でなく、使えるメソッドを制限できます。

バグの元にならないようによく考えて使おう

 今回は型パラメータと同じ機能を実現できる抽象型から始まり、implicitキーワードを用いた暗黙の型変換と暗黙の引数、さらには型パラメータで指定する型の制限を掛けられる可視境界と型パラメータ制約を紹介しました。

 暗黙の型変換や暗黙のパラメータは非常に便利で、うまく使えばコード量を削減して簡潔なプログラミングを可能にしてくれますが、思わぬバグの元になる危険性も持っています。暗黙の型変換/暗黙のパラメータはよく考えて使うようにしましょう。

筆者紹介

クラスメソッド株式会社

中村修太(なかむら しゅうた)

クラスメソッド勤務の新しもの好きプログラマーです。昨年、東京から山口県に引っ越し、現在はノマドワーカーとして働いています。好きなJazzを聴きながらプログラミングするのが大好きです。



前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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