【问题标题】:How to write "asInstanceOfOption" in Scala如何在 Scala 中编写“asInstanceOfOption”
【发布时间】:2020-09-21 20:54:12
【问题描述】:

是否可以编写一个“asInstanceOfOption”方法来执行以下(虚假)代码的意图?

def asInstanceOfOption[T](o: Any): Option[T] =
   if (o.isInstanceOf[T]) Some(o.asInstanceOf[T]) else None 

【问题讨论】:

  • 这是一个很聪明的成语,牛弓的回答也很聪明。

标签: scala


【解决方案1】:

编辑 以下是我的原始答案,但您现在可以使用

def asInstanceOfOption[T: ClassTag](o: Any): Option[T] = 
  Some(o) collect { case m: T => m}

您可以使用清单来解决 T 类型在编译时被擦除这一事实:

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]

那么这个就可以用了:

scala> asInstanceOfOption[Int]("Hello")
res1: Option[Int] = None

scala> asInstanceOfOption[String]("World")
res2: Option[String] = Some(World)

您甚至可以使用 隐式转换 来使其成为Any 上可用的方法。我想我更喜欢方法名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
  }   
}

现在你可以编写如下代码:

"Hello".matchInstance[String] == Some("Hello") //true
"World".matchInstance[Int] == None             //true    

编辑:更新了 2.9.x 的代码,其中不能使用 Any,而只能使用 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
  }   
}

【讨论】:

  • 只有一个观察; “结构类型”使用反射来调用matchInstance 方法。如果性能更关键,您可以使用标准转换为非结构类型
  • matchInstance 方法使用反射调用,因为它不是结构类型,而只是一个匿名类。
  • 现在看来这只适用于 AnyRef -- 我已经发布了一个关于 Any 的问题:stackoverflow.com/questions/7873936/…
  • Kim Stebel:对不起,你实际上是不正确的。幸运的是,带有 -feature 选项的 Scala 2.10 对此给出了警告。
  • 要获得警告,您必须使用添加的方法,如"".matchInstance[String]
【解决方案2】:

这是对 oxbow_lake 更新答案的详细说明,进一步更新为需要 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
    }
  }
}

这可以通过这样做来使用:

"Hello".asInstanceOfOpt[String] == Some("Hello") // true
"foo".asInstanceOfOpt[List[_]] == None // true

但是,正如其他答案中提到的那样,由于装箱问题,这不适用于原语,也不能因为擦除而处理泛型。为了禁止原语,我限制objT 扩展AnyRef。有关处理原语的解决方案,请参阅 Matt R 的后续问题的答案:

How to write asInstanceOfOpt[T] where T <: Any

或者使用shapeless的Typeable,它处理原语以及许多擦除情况:

Type casting using type parameter

【讨论】:

    【解决方案3】:

    在撰写 oxbow_lakes 的答案时(09 年末),我认为 scala.util.Try 不可用,但是,现在(即从 2.10 开始)我认为 scala.util.Try 是首选(或者至少更好-look) 这样做的方式:

    scala> Try((3).asInstanceOf[String]).toOption
    res0: Option[String] = None
    
    scala> Try("hello".asInstanceOf[String]).toOption
    res1: Option[String] = Some(hello)
    

    这依赖于异常,因此可能不是性能关键代码块的首选样式。

    【讨论】:

    • 为什么它比 oxbow_lake 的 Some(x) collect { case m : MyType =&gt; m } 更可取? Try 会引发异常并处理它,这似乎有点过分......
    • @Paul:如果你指的是性能,那么你可能是对的;我只是觉得它更易读,也更容易打字。
    • 好的。可读性当然是个人的,但我认为收集很清楚(更直接地说明了意图)
    • @Paul: 对我来说“暂时将x 转换为SomeType 更具可读性...因为你没有收集任何东西...collect在这种情况下只是一个技术词,Some 也是如此...在我的示例中,只有 toOption 是技术词。
    猜你喜欢
    • 2020-07-31
    • 1970-01-01
    • 2011-09-06
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 2012-02-27
    相关资源
    最近更新 更多