我正在一个项目中,我需要在其中标记接收到的消息的类型。消息可能来自不同的来源,但是所有这些来源都会产生具有相同概念类型(因此含义相同)但以不同方式编写的消息。
例如,我可以从 source1 接收
Source1:
{
"message_type": "typeA",
"value": 3
...
}
或
{
"message_type": "typeB",
"value": 3
...
}
但是我也可以从 source2 接收
Source2:
{
"message_type": "A",
"value": 5
...
}
或
{
"message_type": "B",
"value": 2
...
}
我想最大程度地重复使用代码,因此尝试了此解决方案。
我创建的第一个scala文件是特征:
trait MessageType extends Enumeration {
val TYPE_A: Value
val TYPE_B: Value
}
然后我在两个目标文件中实现了它:
object Source1MessageType extends MessageType{
override val TYPE_A: Value("typeA")
override val TYPE_B: Value("typeB")
object Source2MessageType extends MessageType{
override val TYPE_A: Value("A")
override val TYPE_B: Value("B")
所以现在我想要的是在不知道源类型的情况下检查消息的类型,如下所示:
def foo(type: MessageType.Value) {
type match{
case MessageType.TYPE_A => ...do A action...
case MessageType.TYPE_B => ...do B action...
}
}
但是,如果我编写此代码,则IDE(IntelliJ)以红色突出显示该参数,但没有提供有关该错误的信息。看来我只能使用 Source1MessageType 或 Source2MessageType 作为参数类型。
我认为错误是因为Scala不会将特征视为枚举,所以我无法访问该枚举的值。
您对此有什么解决方案?
答案 0 :(得分:2)
是的,您可以进行层次枚举。因此,通常我建议您不要使用Enumeration
。这是一篇关于它为什么不好的文章
https://medium.com/@yuriigorbylov/scala-enumerations-hell-5bdba2c1216
最惯用的方法是利用类似以下特征的密封特征:
sealed trait MessageType{
def value:String
}
sealed trait MessageType1 extends MessageType
final case object TypeA extends MessageType1{
override def value:String = "typeA"
}
final case object TypeB extends MessageType1{
override def value:String = "typeB"
}
sealed trait MessageType2 extends MessageType
final case object A extends MessageType2{
override def value:String = "A"
}
final case object B extends MessageType2{
override def value:String = "B"
}
请注意,所有这些定义都必须位于同一文件中。现在这行得通,因为sealed
和final
告诉编译器继承只能在该文件中发生。
这意味着在给定MessageType2
实例的情况下,编译器知道它只能是对象A
或B
,而不能是其他任何对象(由于密封/最终)
这使您可以进行模式匹配等方面的详尽检查。
答案 1 :(得分:0)
如果“ A”和“ typeA”是同一消息类型的名称,则在读取数据时需要处理此问题。这意味着您在枚举中不需要任何嵌套。
trait MessageType
case object MessageTypeA extends MessageType
case object MessageTypeB extends MessageType
object MessageType {
def apply(s: String): MessageType =
s match {
case "A" | "typeA" => MessageTypeA
case "B" | "typeB" => MessageTypeB
case _ => throw BadMessageType
}
}
case class Message(msgType: MessageType, value: Int)
您可以使用MessageType(<string>)
创建消息类型,并将根据需要返回MessageTypeA
或MessageTypeB
。您可以使用普通的match
来确定您拥有哪种消息类型。
如果需要保留生成MessageType
的原始字符串,则可以将其作为抽象值存储在MessageType
特征中,并在创建适当的实例时将其填充:
trait MessageType {
def origin: String
}
case class MessageTypeA(origin: String) extends MessageType
case class MessageTypeB(origin: String) extends MessageType
object MessageType {
def apply(s: String): MessageType =
s match {
case "A" | "typeA" => MessageTypeA(s)
case "B" | "typeB" => MessageTypeB(s)
case _ => throw BadMessageType
}
}