2

我正在使用一个将标识符作为字符串传递的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
  }

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

有没有更好的方法来做到这一点,尤其是在性能方面?这种方法有什么问题可能会回来咬我吗?

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

4

2 回答 2

5

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

我不会使用转换直接与 Java 库交互,而是围绕它创建一个包装器,接受并返回您的操作类型。这样你就可以得到一个很好的类型化 api 并且不需要任何转换。

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

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

于 2017-06-19T20:49:11.757 回答
2

与以下方法相比,您的方法有什么好处:?

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 中!(除了我们不能在 or 类型中包含文字单例类型,但我确信它们会在某个时候得到支持,请参阅这个问题)。枚举只是您已经可以对密封特征+案例类执行的操作的语法,除了“节省一些击键”之外,它们不应该有任何帮助。

于 2017-06-20T07:40:53.270 回答