2013-05-29 8 views
5

Se ho un metodo ...Come posso utilizzare la nuova API reflection per stabilire se il tipo di componente di una matrice è conforme a un parametro di tipo?

def arrayConformsTo[A](as: Array[_]) = ??? 

... dove posso aggiungere Contesto Bounds-A come necessario. Voglio questo metodo per guardare il tipo di componente di Array e restituire true se questo è un sottotipo di A. Così, per esempio:

arrayConformsTo[Int](Array(1, 2, 3)) //returns true 

arrayConformsTo[String](Array(1, 2, 3)) //returns false 

Prima di 2.10, questo sarebbe stato fatto come segue:

def arrayConformsTo[A: Manifest](as: Array[_]) = 
    ClassManifest.fromClass(as.getClass.getComponentType) <:< manifest[A] 

Tuttavia adesso compila con warning di deprecazione

<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] 

La mia prima risposta a questo è la seguente:

scala> def arrayConformsTo[A: reflect.ClassTag](as: Array[_]) = 
    | reflect.ClassTag(as.getClass.getComponentType) <:< implicitly[reflect.ClassTag[A]] 

Ma questo dà un avvertimento deprecazione e

<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]] 
               ^

Mi dice di usare TypeTag. Ma come? È forse una cosa valida chiedere di riflettere?


Appendice: questo sembra funzionare abbastanza bene per quello che ho bisogno, anche se non funziona per AnyVal:

scala> def arrayConformsTo[A: reflect.ClassTag](as: Array[_]) = 
    | implicitly[reflect.ClassTag[A]].runtimeClass isAssignableFrom as.getClass.getComponentType 

risposta

7

l'API Scala di riflessione sicuro che è piuttosto un labirinto, ma almeno è completo:

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 
} 

prova REPL:

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 

(Questa modifica di PO per motivi di completezza)

Un'altra soluzione (non richiede)), anche se uno che non conserva la proprietà Float <:< AnyVal is true è quello di utilizzare un ClassTag come un estrattore:

scala> def arrayConformsTo[A](as: Array[_])(implicit arrayOfA: ClassTag[Array[A]]) 
    | = as match { 
    |  case arrayOfA(_) => true 
    |  case _   => false 
    | } 
+0

È grandioso! Se non ti dispiace, ho intenzione di modificare la tua risposta per dare un'altra soluzione prima di accettarla –

1

Questo funziona per me w/o eventuali avvisi del compilatore:

def arrayConformsTo[A](as: Array[_])(implicit t:ClassTag[A]) = { 
    ClassTag(as.getClass().getComponentType()) equals t 
} 

poi questo stampa true e poi false

println(arrayConformsTo[Int](Array(1,2,3))) 
println(arrayConformsTo[String](Array(1,2,3))) 
+1

Ma questo non funziona per '' AnyVal' né Any', proprio come l'ultima implementazione da oxbow_lakes nel suo appendice. –

+1

Buon punto. Mi piace la tua risposta meglio. – cmbaxter