2013-08-17 25 views
8

Dato è un metodo Java che restituisce java.lang.Object s per una determinata stringa. Mi piacerebbe racchiudere questo metodo in un metodo Scala che converte le istanze restituite in un tipo T. Se la conversione fallisce, il metodo dovrebbe restituire None. Sto cercando qualcosa di simile a questo:Tipo di trasmissione utilizzando il parametro tipo

def convert[T](key: String): Option[T] = { 
    val obj = someJavaMethod(key) 
    // return Some(obj) if obj is of type T, otherwise None 
} 

convert[Int]("keyToSomeInt") // yields Some(1) 
convert[String]("keyToSomeInt") // yields None 

(Come) Questo può essere raggiunto utilizzando l'API di riflessione di Scala? Sono ben consapevole che la firma di convert potrebbe dover essere modificata.

risposta

12

Questo è quello che un ClassTag se per:

import reflect.ClassTag 

def convert[T : ClassTag](key: String): Option[T] = { 
    val ct = implicitly[ClassTag[T]] 
    someJavaMethod(key) match { 
    case ct(x) => Some(x) 
    case _ => None 
    } 
} 

Esso può essere utilizzato come un estrattore per testare e cast al tipo appropriato, allo stesso tempo.

Esempio:

scala> def someJavaMethod(s: String): AnyRef = "e" 
someJavaMethod: (s: String)AnyRef 

[...] 

scala> convert[Int]("key") 
res4: Option[Int] = None 

scala> convert[String]("key") 
res5: Option[String] = Some(e) 

Edit: Si noti tuttavia che un ClassTag fa non automaticamente Unbox primitive in scatola. Quindi, ad esempio, convert[Int]("a") non funzionerebbe mai, perché il metodo java restituisce AnyRef, dovrebbe essere convert[java.lang.Integer]("a") e così via per altri tipi primitivi.

La risposta di Miles con Typeable sembra occuparsi automaticamente di tali edge case.

15

Si potrebbe provare shapeless s' Typeable,

scala> import shapeless._ ; import syntax.typeable._ 
import shapeless._ 
import syntax.typeable._ 

scala> def someJavaMethod(key: String): AnyRef = 
    | key match { 
    |  case "keyToSomeInt" => 23.asInstanceOf[AnyRef] 
    |  case "keyToSomeString" => "foo" 
    | } 
someJavaMethod: (key: String)AnyRef 

scala> def convert[T: Typeable](key: String): Option[T] = 
    | someJavaMethod(key).cast[T] 
convert: [T](key: String)(implicit evidence$1: shapeless.Typeable[T])Option[T] 

scala> convert[Int]("keyToSomeInt") 
res0: Option[Int] = Some(23) 

scala> convert[String]("keyToSomeString") 
res1: Option[String] = Some(foo) 

scala> convert[String]("keyToSomeInt") 
res2: Option[String] = None 

scala> convert[Int]("keyToSomeString") 
res3: Option[Int] = None 
+0

Grazie mille per questo. Ho creato il mio 'safeCast [T: ClassTag]' su una conversione implicita da Option [Any] ma ho notato che le primitive restituivano sempre None. Usando Typeable ho cambiato il corpo in un semplice 'opt.flatMap (_. Cast [T])' e funziona alla grande. – ShawnFumo

Problemi correlati