If I have a method...
def arrayConformsTo[A](as: Array[_]) = ???
... where I can add Context Bounds to A
as needed. I want this method to look at the component type of the Array
and return true if this is a subtype of A
. So, for example:
arrayConformsTo[Int](Array(1, 2, 3)) //returns true
arrayConformsTo[String](Array(1, 2, 3)) //returns false
Prior to 2.10, this would have been done as follows:
def arrayConformsTo[A: Manifest](as: Array[_]) =
ClassManifest.fromClass(as.getClass.getComponentType) <:< manifest[A]
However this now compiles with deprecation warnings
<console>:8: warning: method <:< in trait ClassManifestDeprecatedApis is deprecated: Use scala.reflect.runtime.universe.TypeTag for subtype checking instead
ClassManifest.fromClass(as.getClass.getComponentType) <:< manifest[A]
^
<console>:8: warning: value ClassManifest in object Predef is deprecated: Use scala.reflect.ClassTag instead
ClassManifest.fromClass(as.getClass.getComponentType) <:< manifest[A]
My first guess at this is as follows:
scala> def arrayConformsTo[A: reflect.ClassTag](as: Array[_]) =
| reflect.ClassTag(as.getClass.getComponentType) <:< implicitly[reflect.ClassTag[A]]
But this gives a deprecation warning as well
<console>:8: warning: method <:< in trait ClassManifestDeprecatedApis is deprecated: Use scala.reflect.runtime.universe.TypeTag for subtype checking instead
reflect.ClassTag(as.getClass.getComponentType) <:< implicitly[reflect.ClassTag[A]]
^
It tells me to use TypeTag
. But how? Is this even a valid thing to ask of reflection?
Appendix: this seems to work reasonably well for what I need, although it does not work for AnyVal
:
scala> def arrayConformsTo[A: reflect.ClassTag](as: Array[_]) =
| implicitly[reflect.ClassTag[A]].runtimeClass isAssignableFrom as.getClass.getComponentType
The scala reflection api sure is quite a labyrinth, but at least it is comprehensive:
import scala.reflect.runtime.{universe => ru}
def arrayConformsTo[A: ru.TypeTag](as: Array[_]) = {
val mirror = ru.runtimeMirror( getClass.getClassLoader )
val classSym = mirror.classSymbol( as.getClass.getComponentType )
classSym.toType <:< implicitly[ru.TypeTag[A]].tpe
}
REPL test:
scala> arrayConformsTo[Float]( Array[Float]() )
res9: Boolean = true
scala> arrayConformsTo[Int]( Array[Float]() )
res10: Boolean = false
scala> arrayConformsTo[AnyVal]( Array[Float]() )
res11: Boolean = true
scala> arrayConformsTo[AnyVal]( Array[Float]() )
res12: Boolean = true
scala> arrayConformsTo[Any]( Array[Float]() )
res13: Boolean = true
scala> arrayConformsTo[Any]( Array[Float]() )
res14: Boolean = true
scala> arrayConformsTo[AnyRef]( Array[Float]() )
res15: Boolean = false
scala> arrayConformsTo[AnyRef]( Array[Float]() )
res16: Boolean = false
(This edit by the OP for reasons of completeness)
Another solution (not requiring scala-reflect.jar
), albeit one which does not preserve the Float <:< AnyVal is true
property is to use a ClassTag
as an extractor:
scala> def arrayConformsTo[A](as: Array[_])(implicit arrayOfA: ClassTag[Array[A]])
| = as match {
| case arrayOfA(_) => true
| case _ => false
| }
This works for me w/o any compiler warnings:
def arrayConformsTo[A](as: Array[_])(implicit t:ClassTag[A]) = {
ClassTag(as.getClass().getComponentType()) equals t
}
Then this prints true
and then false
println(arrayConformsTo[Int](Array(1,2,3)))
println(arrayConformsTo[String](Array(1,2,3)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With