Comment écrire & # 8220; asInstanceOfOption & # 8221; à Scala
-
05-07-2019 - |
Question
Est-il possible d’écrire un "asInstanceOfOption"? méthode qui ferait ce qui est prévu par le code (faux) suivant?
def asInstanceOfOption[T](o: Any): Option[T] =
if (o.isInstanceOf[T]) Some(o.asInstanceOf[T]) else None
La solution
MODIFIER Voici ma réponse initiale, mais vous pouvez le faire maintenant avec
def asInstanceOfOption[T: ClassTag](o: Any): Option[T] =
Some(o) collect { case m: T => m}
Vous pouvez utiliser des manifestes pour contourner le fait que le type T
est effacé lors de la compilation:
scala> import scala.reflect._
import scala.reflect._
scala> def asInstanceOfOption[B](x : Any)(implicit m: Manifest[B]) : Option[B] = {
| if (Manifest.singleType(x) <:< m)
| Some(x.asInstanceOf[B])
| else
| None
| }
asInstanceOfOption: [B](x: Any)(implicit m: scala.reflect.Manifest[B])Option[B]
Ceci pourrait alors être utilisé:
scala> asInstanceOfOption[Int]("Hello")
res1: Option[Int] = None
scala> asInstanceOfOption[String]("World")
res2: Option[String] = Some(World)
Vous pouvez même utiliser des conversions implicites pour que cette méthode soit disponible sur Any
. Je pense que je préfère le nom de la méthode matchInstance
:
implicit def any2optionable(x : Any) = new { //structural type
def matchInstance[B](implicit m: Manifest[B]) : Option[B] = {
if (Manifest.singleType(x) <:< m)
Some(x.asInstanceOf[B])
else
None
}
}
Vous pouvez maintenant écrire du code tel que:
"Hello".matchInstance[String] == Some("Hello") //true
"World".matchInstance[Int] == None //true
EDIT: code mis à jour pour 2.9.x, dans lequel on ne peut pas utiliser Any
mais seulement AnyRef
:
implicit def any2optionable(x : AnyRef) = new { //structural type
def matchInstance[B](implicit m: Manifest[B]) : Option[B] = {
if (Manifest.singleType(x) <:< m)
Some(x.asInstanceOf[B])
else
None
}
}
Autres conseils
Voici une élaboration de la réponse mise à jour d'oxbow_lake, mise à jour ultérieurement pour nécessiter Scala 2.10:
// Implicit value class
implicit class Castable(val obj: AnyRef) extends AnyVal {
def asInstanceOfOpt[T <: AnyRef : ClassTag] = {
obj match {
case t: T => Some(t)
case _ => None
}
}
}
Ceci peut être utilisé en faisant:
"Hello".asInstanceOfOpt[String] == Some("Hello") // true
"foo".asInstanceOfOpt[List[_]] == None // true
Toutefois, comme indiqué dans d'autres réponses, cela ne fonctionne pas pour les primitives en raison de problèmes de boxe, ni pour les génériques en raison de l'effacement. Pour interdire les primitives, j'ai limité obj
et T
à l'extension de AnyRef
. Pour une solution prenant en charge les primitives, reportez-vous à la réponse de Matt R à la question suivante:
Comment écrire asInstanceOfOpt [T] où T < ;: Any
Ou utilisez le Typeless de Typeable, qui gère les primitives ainsi que de nombreux cas d'effacement:
Au moment de la rédaction de la réponse d'oxbow_lakes (fin 2009), je crois que scala.util.Try
n'était pas disponible, cependant (à compter de la version 2.10), je pense . scala.util.Try
est le moyen préféré (ou du moins plus joli) de le faire:
scala> Try((3).asInstanceOf[String]).toOption
res0: Option[String] = None
scala> Try("hello".asInstanceOf[String]).toOption
res1: Option[String] = Some(hello)