【问题标题】:Scala implicit conversion for object对象的Scala隐式转换
【发布时间】:2018-02-01 04:35:50
【问题描述】:

假设我有以下代码sn-p:

import scala.language.implicitConversions

sealed trait Command {
  val typeName: String
}

object Command {

  implicit def command2String(implicit c: Command): String =
    c.typeName

}

case object SendMessageCommand extends Command {
  override val typeName: String = "send_message"
}

我想比较 StringCommand 后代没有显式转换。例如:

"sendMessage" == SendMessageCommand

Q1:在 Scala 中是否可行?

Q2:我能否定义通用隐式转换,将已知类型(如 String)转换为超类类型(如 Command 在我的例子中)?

例如:

implicit def string2Command(implicit s: String): Command = {
  case "send_message" => SendMessageCommand
  case _ => // some error thrown
}

附:我知道它不是惯用的 Scala,但找到正确的方法会很棒。

【问题讨论】:

    标签: scala implicit-conversion scala-implicits


    【解决方案1】:

    是的,这是可能的,但不适用于==,因为 Scala 仅支持以下情况的隐式转换:转换为预期类型、选择接收者的转换以及隐式参数,因此隐式不适用于比较情况。相反,您必须在Command trait 中创建一个comparison method,如下所示。

      sealed trait Command {
        val typeName: String
        def isEqual(c: Command) = c.typeName == this.typeName
      }
    
      object SendMessageCommand extends Command {
        override val typeName: String = "send_message"
      }
    
      implicit def stringToCommand(s: String) = new Command {override val typeName: String = s}
      implicit def commandToString(c: Command) = c.typeName
    
      val strToCmdCompare = "send_message" isEqual SendMessageCommand
      val cmdToStrCompare = SendMessageCommand isEqual "send_message"
    
      println(strToCmdCompare)    //print true
      println(cmdToStrCompare)    //print true
    

    现在,来到你的第二个问题,是的,可以定义一个通用的隐式转换来将任何给定类型转换为另一个类型实例。但是,据我了解您的情况,您有命令对象列表,并且您希望这些对象中的特定命令具有给定的字符串名称。为此,您必须拥有这些实例列表。

      sealed trait Command {
        val typeName: String
        //This is required for implicit conversion.
        override def toString: String = typeName
      }
    
      object SendMessageCommand extends Command {
        override val typeName: String = "send_message"
      }
      object AddMessageCommand extends Command {
        override val typeName: String = "add_message"
      }
      object UpdateMessageCommand extends Command {
        override val typeName: String = "update_message"
      }
      object DeleteMessageCommand extends Command {
        override val typeName: String = "delete_message"
      }
    
      //List of commands.
      implicit val cmds: List[Command] = List(SendMessageCommand, AddMessageCommand, UpdateMessageCommand, DeleteMessageCommand)
    
      //Convert given type T into type U.
      implicit def convert[T, U](s: T)(implicit list: List[U]): Option[U] = {
        list.find(_.toString == s.toString)
      }
      val res: Option[Command] = "add_message"
      println((res.getOrElse(null) == AddMessageCommand)) //print true
    

    注意:当我通过将它们转换为string 来比较两个类型实例时,我必须在此用例的命令中覆盖toString 方法。如果不是字符串,您可能还需要对类型 T 执行此操作。此外,您可以根据需要在convert method 中实现自己的逻辑,而不是与toString 转换进行比较。

    【讨论】:

    • 一般来说,将这样的一般隐式转换定义为convert 并不是一个好主意。如果您将其指定为 Strings 和 Commands,则也不需要覆盖 Command#toString
    【解决方案2】:

    == 无法做到这一点,但你可以定义自己的运算符:

     case class Foo(s: String) {
       def eq(x: Any) = x match {
         case Foo(str) => str == s
         case str: String => str == s
         cae _ => false
       } 
       def ==: (x: Any) = eq(s)
       def :== (x: Any) = eq(s)
     }
    
     "bar" ==: Foo("bar") // true
     Foo("bar") :== "bar" // true 
     Foo("bar") ==: Foo("bar") // true 
    

    这是因为 scala 中的一个特殊技巧,使名称以冒号结尾的方法绑定到右侧而不是左侧:"bar" ==: Foo("bar") 表示 Foo("bar").:==("bar"),而不是 "bar".:==Foo("bar")

    至于第二个问题,我不确定我是否理解您在问什么......“我可以定义隐式转换吗?”嗯,是的,你可以:)

    【讨论】:

    • 谢谢。 Q2:我的意思是我可以定义一个转换,它不会转换特定类型而是转换它的超类型。如果不够清楚,我会更新问题。
    • 嗯,答案是“是的,你可以”......我还是不明白你为什么要问它。你试过了吗,并得到了某种错误?
    猜你喜欢
    • 1970-01-01
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 2013-03-08
    • 2019-04-01
    • 1970-01-01
    • 2012-04-15
    相关资源
    最近更新 更多