特定のケースで、実際に既存のコードを移行して同じ動作を維持する必要がある場合は、が必要ですClassTag
。使用するTypeTag
方が正確ですが、そのため、一部のコードは異なる動作をするため、(一般的に)注意する必要があります。
本当に必要TypeTag
な場合は、上記の構文よりもさらに優れた方法を実行できます。呼び出しサイトでの効果は、を省略した場合と同じU
です。
推奨される代替案
ポン引きを使用する
Eugeneの答えでは、両方のタイプを綴る必要がありますが、のタイプを推測することが望ましいですthat
。タイプパラメータリストを指定すると、すべてまたはなしのいずれかが指定されます。タイプカリー化が役立つかもしれませんが、メソッドをポン引きする方が簡単なようです。これも2.10で新しく追加されたこの暗黙のクラスを使用して、ソリューションを3行で定義しましょう。
import scala.reflect.runtime.universe._
implicit class MyInstanceOf[U: TypeTag](that: U) {
def myIsInstanceOf[T: TypeTag] =
typeOf[U] <:< typeOf[T]
}
実際、私は、より良い名前(たとえばstat_isInstanceOf
)を持つこのようなものがPredefに属することさえできると主張します。
使用例:
//Support testing (copied from above)
class A
class B extends A
class C
//Examples
(new B).myIsInstanceOf[A] //true
(new B).myIsInstanceOf[C] //false
//Examples which could not work with erasure/isInstanceOf/classTag.
List(new B).myIsInstanceOf[List[A]] //true
List(new B).myIsInstanceOf[List[C]] //false
//Set is invariant:
Set(new B).myIsInstanceOf[Set[A]] //false
Set(new B).myIsInstanceOf[Set[B]] //true
//Function1[T, U] is contravariant in T:
((a: B) => 0).myIsInstanceOf[A => Int] //false
((a: A) => 0).myIsInstanceOf[A => Int] //true
((a: A) => 0).myIsInstanceOf[B => Int] //true
より互換性のある構文
呼び出し構文が変更され、既存のコードがあるためにポン引きが問題になる場合は、次のように型カリー化(使用するのがより難しい)を試すことができます。これにより、1つの型パラメーターのみを明示的に渡す必要がありますAny
。
trait InstanceOfFun[T] {
def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]): Boolean
}
def myIsInstanceOf[T] = new InstanceOfFun[T] {
def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]) =
typeOf[U] <:< typeOf[T]
}
myIsInstanceOf[List[A]](List(new B)) //true
このようなコードを自分で作成する方法を学びたい場合は、以下に示すバリエーションの説明に興味があるかもしれません。
その他のバリエーションと失敗した試行
上記の定義は、構造タイプを使用してよりコンパクトにすることができます。
scala> def myIsInstanceOf[T] = new { //[T: TypeTag] does not give the expected invocation syntax
def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]) =
typeOf[U] <:< typeOf[T]
}
myIsInstanceOf: [T]=> Object{def apply[U](that: U)(implicit evidence$1: reflect.runtime.universe.TypeTag[U],implicit t: reflect.runtime.universe.TypeTag[T]): Boolean}
ただし、-featureが警告するように、構造型を使用することは必ずしも良い考えではありません。
scala> myIsInstanceOf[List[A]](List(new B))
<console>:14: warning: reflective access of structural type member method apply should be enabled
by making the implicit value language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scala docs for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.
myIsInstanceOf[List[A]](List(new B))
^
res3: Boolean = true
問題は、構造タイプを実装するために必要な、反射による速度低下です。上記のように、修正は簡単で、コードが少し長くなります。
避けなければならなかった落とし穴
上記のコードでは、最初の試み[T]
の代わりに書きます。[T: TypeTag]
なぜ失敗するのか興味深い。それを理解するには、以下を見てください。
scala> def myIsInstanceOf[T: TypeTag] = new {
| def apply[U: TypeTag](that: U) =
| typeOf[U] <:< typeOf[T]
| }
myIsInstanceOf: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])Object{def apply[U](that: U)(implicit evidence$2: reflect.runtime.universe.TypeTag[U]): Boolean}
戻り値のタイプを注意深く見ると、それがimplicit TypeTag[T] => U => implicit TypeTag[U]
(疑似Scala表記で)あることがわかります。引数を渡すと、Scalaはそれが最初のパラメーターリストである暗黙のパラメーターリストであると見なします。
scala> myIsInstanceOf[List[A]](List(new B))
<console>:19: error: type mismatch;
found : List[B]
required: reflect.runtime.universe.TypeTag[List[A]]
myIsInstanceOf[List[A]](List(new B))
^
先端
最後に、興味があるかもしれないし興味がないかもしれない1つのヒント:この試みでは、TypeTag [T]を2回渡しています。したがって、の: TypeTag
後に削除する必要があります[T
。
def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) =
myInstanceToTpe(that) stat_<:< tag.tpe