倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Scala の暗黙の型変換ちょっこっとメモ

Scala の暗黙の型変換を使っててちょっと意外だった挙動をメモ。 暗黙の型変換に関するドキュメントとか全く呼んでないので当たり前の話かも知れませんが。

次のような HelloWorld なよくあるクラス Greeter, GreeterR があったとします:

class Greeter{
  def greet(name: String): String = "Hello, " + name + "!"
}

class GreeterR extends Greeter{
  def greet(n: Int): String = "Hello, world" + ("!" * n)
}

GreeterR クラスは Greeter クラスを拡張してます。 使い方は簡単。

val g = new Greeter
assert(g.greet("waman") == "Hello, waman!")

val gr = new GreeterR
assert(gr.greet("waman") == "Hello, waman!")
assert(gr.greet(5) == "Hello, world!!!!!")

GreeterR オブジェクトの greet() メソッドはもちろん引数が String でも Int でも使えますね。

さて、ここで次のような Person クラスと、それに関する変換メソッドを定義しましょう:

case class Person(name: String, age: Int)

object Person{
  import scala.language.implicitConversions  // implicit 有効

  implicit def toString(person: Person): String = person.name
  implicit def toInt(person: Person): Int = person.age
}

で、Person コンパニオン・オブジェクトによる暗黙の型変換を有効にして Greeter や GreeterR オブジェクトに対して Person オブジェクトを引数にして greet() メソッドを呼び出してみましょう:

import scala.language.implicitConversions
import Person._

val me = Person("waman", 10)

val g = new Greeter
assert(g.greet(me) == "Hello, waman!")

val gr = new GreeterR
assert(gr.greet(me) == "Hello, world!!!!!!!!!!")

当初、GreeterR クラスには継承されたメソッドを含めて、引数が String と Int の greet() メソッドがあるので、上記のような Person オブジェクトを String にも Int に暗黙に変換するメソッドがある場合にはコンパイル・エラーになるかと思ってたんですが、実際には宣言されているメソッドが、継承しているメソッドより優先して型変換に使われるようです。

ちなみに、Person オブジェクトの toInt() メソッドをコメントアウトして同様のコードを実行すると

object Person{
  import scala.language.implicitConversions  // implicit 有効

  implicit def toString(person: Person): String = person.name
  //implicit def toInt(person: Person): Int = person.age
}

val me = Person("waman", 10)
val gr = new GreeterR
assert(gr.greet(me) == "Hello, waman!")  // 「"Hello, world!!!!!!!!!!"」ではない

のように、String を引数にとる Greeter クラスの greet() メソッドが呼ばれます。 また、GreeterR クラスに Greeter の greet() メソッドをオーバライドするコードを追加して

class GreeterR extends Greeter{
  override def greet(name: String): String = super.greet(name)
  def greet(n: Int): String = "Hello, world" + ("!" * n)
}

(Person オブジェクトの toInt() メソッドのコメントもはずして)上記のサンプルを実行しようとするとコンパイル・エラーが出ます。 まぁ、この辺りは予想通りの挙動ですね。

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

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

  • 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
  • 出版社/メーカー: インプレスジャパン
  • 発売日: 2011/09/27
  • メディア: 単行本(ソフトカバー)
  • 購入: 12人 クリック: 235回
  • この商品を含むブログ (46件) を見る