2011-10-24 13 views
9

Istnieje przydatna implementacja asInstanceOfOpt, bezpiecznej wersji asInstanceOf, podanej w odpowiedzi na How to write "asInstanceOfOption" in Scala. Wydaje się, że Scala 2.9.1, rozwiązanie to teraz działa tylko z AnyRef:Jak napisać AsInstanceOfOpt [T] gdzie T <: Any

class WithAsInstanceOfOpt(obj: AnyRef) { 
    def asInstanceOfOpt[B](implicit m: Manifest[B]): Option[B] = 
    if (Manifest.singleType(obj) <:< m) 
     Some(obj.asInstanceOf[B]) 
    else 
     None 
} 

Może to być zapisane do obsługi Wszelkie?

Odpowiedz

1

Jeśli przyjrzeć się interfejsowi API Scala, funkcja singleType przyjmuje parametr typu AnyRef. Tak naprawdę nie znam tła tej decyzji, ale wydaje mi się, że trzeba ją obejść. Zamiast korzystać z metody singleType sugeruję użycie metody classType, która w zasadzie może utworzyć manifest dla dowolnej klasy. Zajmie to trochę więcej kodu, ale może wyglądać mniej więcej tak:

class WithAsInstanceOfOpt(obj : Any) { 
    def asInstanceOfOpt[B : Manifest] : Option[B] = // [B : Manifest] is shorthand for [B](implicit m : Manifest[B]) 
    if (Manifest.classType(manifest, obj.getClass) <:< manifest) 
     Some(obj.asInstanceOf[B]) 
    else None 
} 
+0

Powyższy kod nie działa z powodu boksowania. '1.asInstanceOfOpt [Int]' zwraca 'None', podczas gdy' 1.asInstanceOfOpt [java.lang.Integer] 'zwraca' Some (1) 'with type' Option [Integer] '. Trzeba ręcznie odwzorować typy (opublikuję rozwiązanie). – Blaisorblade

0

Oto działający kod dla 2.9.x. Poda ostrzeżenia o przestarzałości w wersji 2.10.x, ale zastąpi je ClassTag zamiast Manifest i runtimeClass zamiast erasure.

wyniki
//Precondition: classS must have been produced through primitiveToBoxed, because v will be boxed. 
def ifInstanceOfBody[T, S](v: T, classS: Class[_]): Option[S] = { 
    if (v == null || !classS.isInstance(v)) 
    None 
    else 
    Some(v.asInstanceOf[S]) 
} 
object ClassUtil { 
    import java.{lang => jl} 

    private val primitiveToBoxedMap = Map[Class[_], Class[_]](
    classOf[Byte] -> classOf[jl.Byte], 
    classOf[Short] -> classOf[jl.Short], 
    classOf[Char] -> classOf[jl.Character], 
    classOf[Int] -> classOf[jl.Integer], 
    classOf[Long] -> classOf[jl.Long], 
    classOf[Float] -> classOf[jl.Float], 
    classOf[Double] -> classOf[jl.Double], 
    classOf[Boolean] -> classOf[jl.Boolean], 
    classOf[Unit] -> classOf[jl.Void] 
) 
    def primitiveToBoxed(classS: Class[_]) = 
    primitiveToBoxedMap.getOrElse(classS, classS) 
} 
class IfInstanceOfAble[T](v: T) { 
    def asInstanceOfOpt[S](implicit cS: Manifest[S]): Option[S] = 
    ifInstanceOfBody[T, S](v, ClassUtil.primitiveToBoxed(cS.erasure)) 
} 
implicit def pimpInstanceOf[T](t: T) = new IfInstanceOfAble(t) 

Testowanie:

scala> 1.asInstanceOfOpt[Int] 
res9: Option[Int] = Some(1) 

scala> "".asInstanceOfOpt[String] 
res10: Option[String] = Some() 

scala> "foo".asInstanceOfOpt[String] 
res11: Option[String] = Some(foo) 

scala> 1.asInstanceOfOpt[String] 
res12: Option[String] = None 

scala> "".asInstanceOfOpt[Int] 
res13: Option[Int] = None 

Kod jest nieco bardziej gadatliwy niż potrzeba tutaj, głównie dlatego wziąłem go z istniejącym kodzie kopalni, gdzie mogę używać ifInstanceOfBody gdzie indziej. Podpisanie pod asInstanceOfOpt naprawiłoby to i nieco skróciło kod, ale większość jest dla primitiveToBoxedMap i ufam, że nie mogłem znaleźć czegoś takiego dostępnego w standardowej bibliotece Scala.

0

Można użyć shapeless „s typeable z Miles Sabin:

Type casting using type parameter

Zajmuje prymitywów i boks:

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

scala> 1.cast[Int] 
res1: Option[Int] = Some(1) 

scala> 1.cast[String] 
res2: Option[String] = None 

scala> "hello".cast[String] 
res4: Option[String] = Some(hello) 

scala> "foo".cast[Int] 
res5: Option[Int] = None 

Można zobaczyć źródło tutaj, aby zobaczyć, jak to jest napisane:

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeable.scala

Powiązane problemy