Como escrever “asInstanceOf Option” em Scala
-
05-07-2019 - |
Pergunta
É possível escrever um método "asInstanceOfOption" que faria o que se pretende com o código a seguir (falso)?
def asInstanceOfOption[T](o: Any): Option[T] =
if (o.isInstanceOf[T]) Some(o.asInstanceOf[T]) else None
Solução
Editar Abaixo está a minha resposta original, mas você pode fazer isso agora com
def asInstanceOfOption[T: ClassTag](o: Any): Option[T] =
Some(o) collect { case m: T => m}
Você pode usar manifesta de contornar o fato de que o tipo T
é Erased em tempo de compilação:
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]
Então isso poderia ser usado:
scala> asInstanceOfOption[Int]("Hello")
res1: Option[Int] = None
scala> asInstanceOfOption[String]("World")
res2: Option[String] = Some(World)
Você poderia até usar conversões implícitas para obter este é um método acessível em Any
. Acho que eu prefiro o nome do método 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
}
}
código Agora você pode escrever como:
"Hello".matchInstance[String] == Some("Hello") //true
"World".matchInstance[Int] == None //true
EDIT: código atualizado para 2.9.x, onde não se pode usar Any
mas apenas 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
}
}
Outras dicas
Aqui está uma elaboração sobre a resposta actualizado da oxbow_lake, mais atualizados para exigir 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
}
}
}
Isto pode ser usado fazendo:
"Hello".asInstanceOfOpt[String] == Some("Hello") // true
"foo".asInstanceOfOpt[List[_]] == None // true
No entanto, como mencionado em outras respostas isso não funcionar para primitivos por causa de problemas de boxe, nem lidar com os genéricos por causa de apagamento. Para primitivas Desautorizar, eu restrito obj
e T
para estender AnyRef
. Para uma solução que lida com primitivas, consultar a resposta para a pergunta de acompanhamento de Matt R:
Como escrever asInstanceOfOpt [T], onde T <: Qualquer
Ou uso disforme 's tipável, que lida com primitivas, bem como muitos casos de apagamento:
No momento da escrita da resposta de oxbow_lakes (final de '09), acredito scala.util.Try
não estava disponível, no entanto, agora (ou seja, a partir de 2.10) Eu acho scala.util.Try
é o (ou bem, pelo menos, mais bonito) forma preferida de fazer isso:
scala> Try((3).asInstanceOf[String]).toOption
res0: Option[String] = None
scala> Try("hello".asInstanceOf[String]).toOption
res1: Option[String] = Some(hello)