LoginSignup
34
26

More than 5 years have passed since last update.

Scalaで型消去を回避する

Posted at

詳しいことはこちらを参照 => 型タグとマニフェスト

クラスや関数で実行時に指定される抽象型の情報は、Javaの型消去により判定できなくなってしまう。

例えば以下のScalaコードでは、型Tの情報はコンパイル時に消えている。

class A
class B extends A
class C extends A

def isType[T <: A](a: A) = a.isInstanceOf[T]
// <console>:9: warning: abstract type T is unchecked since it is eliminated by erasure
//        def isType[T <: A](a: A) = a.isInstanceOf[T]
//                                                 ^
// isType: [T <: A](a: A)Boolean

型消去により意図しない挙動になる可能性があることは、コンパイル時の警告でチェックできる。

実際に動作させてみても、型の検出はできない。

scala> isType[A](new A)
res11: Boolean = true

scala> isType[C](new B)
res12: Boolean = true  // 本当は (new B).isInstanceOf[C] => false

ClassTagで型情報を保持する

Scalaの場合、暗黙のパラメータで型の情報を保持することができる。

def isType[T <: A](a: A)(implicit e: scala.reflect.ClassTag[T]) = e.runtimeClass.isInstance(a)
//=> isType: [T <: A](a: A)(implicit e: scala.reflect.ClassTag[T])Boolean

処理中にe.runtimeClassを使って具体的な Class[T] を取得し、リフレクションで型チェックを行っている。

scala> isType[C](new B)
res13: Boolean = false

context boundを使う

型パラメータ自体に context bound というシンタックスシュガーが用意されている。

import scala.reflect.ClassTag

def isType[T <: A : ClassTag](a: A) = implicitly[ClassTag[T]].runtimeClass.isInstance(a)

定義している内容としては暗黙のパラメータと同じだが、引数の名前が無く、代わりに暗黙の値を implicitly で取得する事ができる。

パターンマッチに使う

implicitly を使って型情報を取得しなくても、パターンマッチで型チェックをすると自動的に処理してくれるようだ。

import scala.reflect.ClassTag

def isType[T <: A : ClassTag](a: A) = a match {
  case t: T => true
  case _ => false
}

個人的にはこれがシンプルで好み。

34
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
26