我正在使用将标识符作为字符串传递的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可能是更好的选择)。
答案 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)。枚举只是你已经可以使用密封特征+案例类的语法,他们不应该帮助除了“保存几个键击”之外的任何东西。