优化案例类用作符号

时间:2017-06-19 20:03:24

标签: scala dotty

我正在使用将标识符作为字符串传递的Java API。对我来说,使用类型符号似乎更好一些,所以我写了这个:

  object Helpers {
    implicit def actionToString(action: Action): String =
      action.getClass.getName.stripSuffix("$").replaceAll(".*\\$", "")

    object Action{
      def apply(name: String): Action = {
        Class.forName("tutorial.HelloInput$Helpers$" + name).newInstance()
          .asInstanceOf[Action]
      }
    }
    sealed abstract class Action {
      override def toString: String = actionToString(this)
    }
    final case class Rotate() extends Action
    final case class Right() extends Action
    final case class Left() extends Action
    final case class Pause() extends Action
  }

这允许"序列化"和#34;反序列化"字符串和动作以自然的方式,例如,我可以在Action(Pause)上进行模式匹配,但我也可以将Pause()传递给期望字符串的Java库,这要归功于隐式转换。

有没有更好的方法来做到这一点,特别是在性能方面?这种方法有什么问题可能会让我以后再咬一口吗?

我正在阅读关于Dotty中Phantom类型的一些内容,并想知道它们是否可能用于提高处理符号的性能(或者新的Enums可能是更好的选择)。

2 个答案:

答案 0 :(得分:4)

隐性转换习惯会回来咬你。在这种情况下,这意味着您可以在需要String的任何方法中使用您的某种操作类型,而不仅仅是您希望它的位置。它也不会阻止您将其他字符串传递到库中。

我不是直接使用转换来与Java库接口,而是创建一个包装它的包装器,它接受并返回你的动作类型。通过这种方式,您可以获得一个不错的类型api,无需任何转换。

由于您的类没有参数,因此使用大小写对象是有意义的,因此每个动作类型只需要一个实例,而不是每次都创建一个新实例。

反射的使用也关系到我。从字符串创建动作可以使用模式匹配来实现,并且可以通过让每个类型定义它的字符串值来完成到字符串的转换,或者如果类的名称总是与之相同,则甚至只使用productPrefix。你需要的字符串(虽然我更愿意明确定义它)。我怀疑这种方法也会更快,但你需要对它进行基准测试才能确定。

答案 1 :(得分:1)

与以下方法相比,您的方法有哪些好处:?

object Helpers {
  type Action = String
  val Rotate: Action = "Rotate"
  val Right:  Action = "Right"
  val Left:   Action = "Left"
  val Pause:  Action = "Pause"
}

您可以使用值类交换样板安全性(不会失去任何性能)样板:

object Helpers {
  // This won't allocate anything more than the previous solution!
  final case class Action(name: String) extends AnyVal
  val Rotate: Action = Action("Rotate")
  val Right:  Action = Action("Right")
  val Left:   Action = Action("Left")
  val Pause:  Action = Action("Pause")
}

我认为上述两种方法都比使用反射+隐式转换更强大。例如,当移动代码或重命名tutorial包时,您的解决方案将无声地中断。

  

这种方法有什么问题可能会让我以后再咬一口吗?

隐性转换是魔鬼。我强烈建议你永远不要在你的设计中考虑它们,它很容易“得到一些有用的东西”并且在一两个星期之后后悔...事实上,你的解决方案以下编译将是IMO,这是不可接受的:

val myAction: Action = ...
myAction.foreach(...)
myAction.map(...)
  

我正在阅读有关Dotty中Phantom类型的一些内容,并想知道它们是否可能用于提高处理符号的性能(或者新的Enums可能是更好的选择)。

我认为在这种情况下,联合类型+文字单例类型可能很有用,您可以定义以下类型别名:

type Action = "Rotate" | "Right" | "Left" | "Pause"

然后,如果一个方法只能用这些类型的子集调用,那么你的情况就是非常精确地将它放在它的API中! (除了我们不能在文字或类型中使用文字单例类型,但我确定它们会在某些时候得到支持,请参阅this issue)。枚举只是你已经可以使用密封特征+案例类的语法,他们不应该帮助除了“保存几个键击”之外的任何东西。