21

オブジェクトが の型のインスタンスであるかどうかを知ることができる関数がありますManifest。バージョンに移行したいと思いTypeTagます。古い関数は次のとおりです。

def myIsInstanceOf[T: Manifest](that: Any) = 
  implicitly[Manifest[T]].erasure.isInstance(that)

私は TypeTags を試してきましたが、今は TypeTag バージョンを持っています:

// Involved definitions
def myInstanceToTpe[T: TypeTag](x: T) = typeOf[T]
def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) = 
  myInstanceToTpe(that) stat_<:< tag.tpe

// Some invocation examples
class A
class B extends A
class C

myIsInstanceOf(typeTag[A], new A)        /* true */
myIsInstanceOf(typeTag[A], new B)        /* true */
myIsInstanceOf(typeTag[A], new C)        /* false */

このタスクを達成するためのより良い方法はありますか? U代わりにan を使用して、パラメータ化されたものを省略できますかAny(古い関数で行われるのと同じように)?

4

4 に答える 4

22

消去されたタイプにサブタイピングチェックを使用するだけで十分な場合は、上記のコメントでTravisBrownが提案したように実行してください。

def myIsInstanceOf[T: ClassTag](that: Any) =
  classTag[T].runtimeClass.isInstance(that)

それ以外の場合は、型を明示的にU記述して、scalacがその型をtypeタグにキャプチャするようにする必要があります。

def myIsInstanceOf[T: TypeTag, U: TypeTag] =
  typeOf[U] <:< typeOf[T]
于 2012-07-24T10:54:59.073 に答える
10

特定のケースで、実際に既存のコードを移行して同じ動作を維持する必要がある場合は、が必要です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
于 2012-07-26T17:08:55.420 に答える
0
于 2014-09-26T07:42:00.503 に答える
0

上記の提案を使用して、次のことを考え出しました。フィードバックを歓迎します。

/*
    Attempting to cast Any to a Type of T, using TypeTag
    http://stackoverflow.com/questions/11628379/how-to-know-if-an-object-is-an-instance-of-a-typetags-type
     */
    protected def toOptInstance[T: ClassTag](any: Any) =
        classTag[T].runtimeClass.isInstance(any) match {
            case true =>
                Try(any.asInstanceOf[T]).toOption
            case false =>
                /*
                Allow only primitive casting
                 */
                if (classTag[T].runtimeClass.isPrimitive)
                    any match {
                        case u: Unit =>
                            castIfCaonical[T](u, "void")
                        case z: Boolean =>
                            castIfCaonical[T](z, "boolean")
                        case b: Byte =>
                            castIfCaonical[T](b, "byte")
                        case c: Char =>
                            castIfCaonical[T](c, "char")
                        case s: Short =>
                            castIfCaonical[T](s, "short")
                        case i: Int =>
                            castIfCaonical[T](i, "int")
                        case j: Long =>
                            castIfCaonical[T](j, "long")
                        case f: Float =>
                            castIfCaonical[T](f, "float")
                        case d: Double =>
                            castIfCaonical[T](d, "double")
                        case _ =>
                            None
                    }
                else None
        }

    protected def castIfCaonical[T: ClassTag](value: AnyVal, canonicalName: String): Option[T] ={
        val trueName = classTag[T].runtimeClass.getCanonicalName
        if ( trueName == canonicalName)
            Try(value.asInstanceOf[T]).toOption
        else None
    }
于 2013-12-18T19:16:02.593 に答える