scala - referencias - ¿Cómo saber si un objeto es una instancia del tipo de TypeTag?
referencias a objetos e instancias en poo (4)
En su caso específico, si realmente necesita migrar el código existente y mantener el mismo comportamiento, desea ClassTag
. Usar TypeTag
es más preciso, pero exactamente por eso, algunos códigos se comportarán de manera diferente, por lo que (en general) debe tener cuidado.
Si realmente desea TypeTag
, podemos hacerlo incluso mejor que la sintaxis anterior; el efecto en el sitio de la llamada es el mismo que omitiendo U
Alternativas recomendadas
Usando el proxenetismo
Con la respuesta de Eugene, uno tiene que escribir ambos tipos, mientras que es deseable deducir el tipo de that
. Dada una lista de parámetros de tipo, se especifica todo o ninguno; El tipo de curry podría ayudar, pero parece más simple simplemente pinchar el método. Utilicemos para estas clases implícitas, también nuevas en 2.10, para definir nuestra solución en solo 3 líneas .
import scala.reflect.runtime.universe._
implicit class MyInstanceOf[U: TypeTag](that: U) {
def myIsInstanceOf[T: TypeTag] =
typeOf[U] <:< typeOf[T]
}
De hecho, argumentaría que algo como esto, con un nombre mejor (por ejemplo, stat_isInstanceOf
), podría incluso pertenecer a Predef.
Use ejemplos:
//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
Una sintaxis más compatible.
Si el proxenetismo es un problema porque cambia la sintaxis de invocación y usted tiene un código existente, podemos probar el tipo de currying (más complicado de usar) de la siguiente manera, de modo que solo se deba pasar un parámetro de tipo explícitamente, como en su definición anterior con 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
Si desea aprender a escribir dicho código usted mismo, puede estar interesado en la discusión de las variaciones que se muestran a continuación.
Otras variaciones y intentos fallidos.
La definición anterior se puede hacer más compacta con tipos estructurales:
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}
Sin embargo, el uso de tipos estructurales no siempre es una buena idea, como advierte -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
El problema es la desaceleración debida a la reflexión, necesaria para implementar tipos estructurales. Resolverlo es fácil, solo alarga un poco el código, como se vio anteriormente.
Una trampa que tuve que evitar
En el código anterior, escribo [T]
lugar de [T: TypeTag]
, mi primer intento. Es interesante por qué falla. Para entender eso, eche un vistazo:
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}
Si observa detenidamente el tipo del valor de retorno, puede ver que es implicit TypeTag[T] => U => implicit TypeTag[U]
(en notación pseudo-Scala). Cuando pase un argumento, Scala pensará que es para la primera lista de parámetros, la implícita:
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))
^
Una punta
Lo último y lo menos, una sugerencia que podría o no interesarte: en este intento, estás pasando TypeTag [T] dos veces; por lo tanto, debes eliminar : TypeTag
después de [T
def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) =
myInstanceToTpe(that) stat_<:< tag.tpe
Tengo una función que puede saber si un objeto es una instancia del tipo de Manifest
. Me gustaría TypeTag
a una versión de TypeTag
. La antigua función es la siguiente:
def myIsInstanceOf[T: Manifest](that: Any) =
implicitly[Manifest[T]].erasure.isInstance(that)
He estado experimentando con TypeTags y ahora tengo esta versión de 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 */
¿Hay alguna mejor manera de lograr esta tarea? ¿Se puede omitir la U
parametrizada, utilizando un Any
lugar (tal como se hace en la función antigua)?
Si es suficiente con el uso de chequeos de subtipos en tipos borrados, haga lo que Travis Brown sugirió en el comentario anterior:
def myIsInstanceOf[T: ClassTag](that: Any) =
classTag[T].runtimeClass.isInstance(that)
De lo contrario, debe deletrear explícitamente el tipo U
, para que scalac capture su tipo en una etiqueta de tipo:
def myIsInstanceOf[T: TypeTag, U: TypeTag] =
typeOf[U] <:< typeOf[T]
También puede capturar el tipo de TypeTag (en alias de tipo), pero solo si no se borra, por lo que no funcionará dentro de la función:
¿Cómo capturar T de TypeTag [T] o cualquier otro genérico en scala?
Utilicé las sugerencias anteriores para llegar a lo siguiente. La retroalimentación es bienvenida.
/*
Attempting to cast Any to a Type of T, using TypeTag
http://.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
}