我尝试建模的场景如下。我有几个case类,它们的参数不同,但是它们都扩展了trait Entity
。
// case classes
trait Entity
case class E1(..params..) extends Entity
case class E2(..params..) extends Entity
...
case class En(..params..) extends Entity
我有一组带有一个参数的函数,该参数是的子类型Entity
,如下所示(我们有比实体更多的功能):
// functions using case classes as parameters
def f1(val p:E1) = ???
def f2(val p:E4) = ???
...
def fm(val p:E2) = ???
现在,我得到了一个Entity
序列化为String的实例,在它旁边,我得到了要调用它的函数的名称。反序列化不是问题:假设我有一个read[T](str)
可以反序列str
化为type对象的函数T
。
我想编写一个通用的Scala代码,鉴于这两个字符串(函数名称,序列化实体),可以在反序列化实体后调用正确的函数。
我以为,我需要像下面这样的映射,给定一个函数名称将给我函数本身及其参数的类型。然后,原则上我应该可以轻松地拨打如下电话。
// the mappings from String to entity and corresponding function
val map1 = Map (
"f1" -> f1
"f2" -> f2
...
"fn" -> fn
)
}
val map2 = Map (
"f1" -> E1
"f2" -> E4
...
"fn" -> E2
)
}
def makeTheCall (fname: String, ent: String) = map1.get(fname)(read[map2.get(fname)](ent))
这是行不通的,因为我无法正确选择类型(而且推断的类型也无法正常工作)。
有没有把办法map1
和map2
在一起(这样有少搞乱的功能和参数类型之间的关系的机会)?
编辑:为简单起见,我们在这里可以忽略实体的参数,因此也可以忽略实际的序列化实体。这应该有助于编写无需太多工作的可编译代码。
编辑:用例:我正在编写一个从RabbitMQ接收消息的程序。消息的主体包含该实体,并且消息密钥暗含了对该实体的处理方式。
编辑2:使用与我先前的编辑功能类似的功能,可以创建一个地图,String => Unit
该地图的功能将反序列化和您的函数结合在一起,因此您不需要两个地图。
def deserializeAnd[E <: Entity](f: E => Unit): String => Unit =
(s: String) => f(read[E](s))
val behaviour = Map(
"key1" -> deserializeAnd(println(_: Foo)),
"key2" -> deserializeAnd(println(_: Bar)),
"key3" -> deserializeAnd((foo: Foo) => println(foo.copy(a=0))
)
def processMessage(key: String, serialized: String): Option[Unit] =
behaviour.get(key).map(f => f(serialized))
// throws an exception if 'behaviour' doesn't contain the key
def processMessage2(key: String, serialized: String): Unit =
behaviour(key)(serialized)
编辑:似乎您可能具有相同输入类型的多个功能,这对于类型类而言不是一个好用例。
您可以使用类似:
def makeTheCall[E <: Entity, Out](f: E => Out, s: String): Out = f(read[E](s))
它将字符串反序列化为所传递函数的输入类型。
您可以将其用作makeTheCall(f2, "serializedE4")
。
即使可以找到合适的类型来使makeTheCall
方法正常工作,也不应使用字符串来区分多个类型。如果您在fname
,map1
包含fname
和map2
不包含...中打错了怎么办?
从您的问题中并不清楚您要确切执行什么操作,但似乎类型类将非常适合您的情况。与类型类,你可以创建一个类型的,像你想与你做哪些可以做一些特定功能的情况下f1
,f2
...等功能。
假设您的f1
,,f2
...函数都返回Int
,我们可以创建一个类型类,其中包含此类用于Entity
类型的函数:
trait EntityOperation[E <: Entity] {
def func(e: E): Int
}
让我们创建一些扩展的case类Entity
:
trait Entity
case class Foo(a: Int, b: Int) extends Entity
case class Bar(c: String, d: String) extends Entity
现在,我们可以为Foo
和创建类型类的实例Bar
:
implicit val FooEntityOp = new EntityOperation[Foo] {
def func(foo: Foo) : Int = foo.a + foo.b
}
implicit val BarEntityOp = new EntityOperation[Bar] {
def func(bar: Bar) : Int = bar.c.length + bar.d.length
}
我们可以如下使用我们的类型类:
def callF[E <: Entity](e: E)(implicit op: EntityOperation[E]) = op.func(e)
callF(Foo(1, 2)) // Int = 3
callF(Bar("xx", "yyyy")) // Int = 6
在您的情况下,可能看起来像:
def makeTheCall[E <: Entity](s: String)(implicit op: EntityOperation[E]) =
op.func(read[E](s))
// makeTheCall[Baz]("serializedBaz")
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句