読者です 読者をやめる 読者になる 読者になる

opamp_sando's blog

クソザコが割りと適当なことを書くためにある備忘録です。あとたまに普通の日記も書きます

scalaの暗黙の型変換(implicit conversion)

scala

implicitについていろいろメモる。主にimplicit defとimplicit classについて。

implicit def

implicit defは暗黙の型変換を自分で定義できるというもの。
例えば...

$ scala

としてインタプリタを起動し次のようにしてみると...

> val a:Int = 1.1
<console>:7: error: type mismatch;
found   : Double(1.1)
required: Int
   val a:Int = 1.1  

という具合にerrorが出てしまう。だが、例えば次のよう関数を定義しておくと...

implicit def roundDouble(d:Double):Int = math.floor(i + 0.5).toInt //Doubleな引数を四捨五入してIntにする関数
> val a:Int = 1.1
a: Int = 1

> val b:Int = 1.5
b: Int = 2

というふうに四捨五入してDoubleをIntに暗黙に変換してくれるようになる。
このようにimplicitを使って関数を定義すると暗黙の型変換を定義することができる。

クラスのパラメータを暗黙に渡す

また、クラスに使うパラメータにimplicitを指定することもできる。

class Hoge(implicit text: String){
    println(text)
}

というクラスがあった場合、普通ならインスタンス化するときにString型のパラメータを渡すことで値を渡すことができるが、ここでimplicitを使った場合

implicit val str:String = "hello world" //valにもimplicitが必要
new Hoge //パラメータに自動で上のstrが選ばれる

とすると"hello world"と表示される。このようにパラメータを暗黙のうちに渡すことができる。
ちなみにこれはパラメータリストごとに適応されるよう。つまり、

class Hoge(text1:String, implicit text2:String){ .... }

ということはできない。このようにしたい場合は

class Hoge(text1:String)(implicit text2:String){...}

とする必要がある。

implicit class

Scala2.10で追加されたらしいimplicit classについて...
とりあえず、scala documentにあるコードをお借りして

Object Helpers{
  implicit class IntWithTimes(x: Int) {
    def times[A](f: => A){
  
      def loop(current: Int){
        if(current > 0) {
          f
          loop(current - 1)
        }
      }

      loop(x)
    }
  }
}

このようにクラスにimplicitをつける。ちなみにimplicit classはtrait,classまたはobjectの中に定義しないといけない。(scalaインタプリタなら直接定義できる)

上のようなクラスを定義している状態で

import Helpers._

6 times println("Happy Birthday Miku!")

とすると

Happy Birthday Miku!
Happy Birthday Miku!
Happy Birthday Miku!
Happy Birthday Miku! 
Happy Birthday Miku!
Happy Birthday Miku!

と、与えられた関数を6回実行してくれる。

これは本来は

import Helpers._
(new IntWithTimes(6)).times {println("hello")}

というふうに書く必要がある。 implicit classを使うことで"(new IntWithTimes(6))"を"6"と省略することができる。

また、ルールとして上にも書いたがtrait/class/objectのいずれかの中に定義される必要があり、さらに1つだけimplicitでないパラメータを持たなければいけない。

implicit class Hoge(i:Int,n:Int)
//-> error: implicit classes must accept exactly one primary constructor parameter

とすることはできない

implicit class Hoge(i:Int) //OK
implicit class Piyo(n:Int)(implicit s:String) //OK

最後のルールとして、同じスコープ内にある変数やオブジェクトやメソッドなどと同名ではいけない。 また、case classは自動でコンパニオンオブジェクトを作るためcase classをimplicitとすることもできない。

終わり

という具合に本とScala Documentを見ながら簡単にまとめてみた。
間違いなどがあればご指摘いただけると幸いです。間違いがあった場合は修正や追記をさせていただきますorz

Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

Firefox ブラウザ無料ダウンロード