【问题标题】:Scala Generics vs AnyScala 泛型与任何
【发布时间】:2015-07-30 18:48:12
【问题描述】:

在我正在处理的代码中,我有一个名为 SuccessMessage 的类,其中包含字段 actorType(一个枚举)和有效负载。我不确定要制作什么类型的有效载荷。

如果我制作 Any 类型的有效负载,我必须做很多看起来很粗糙的 instanceOf 类型转换,以便稍后在我的代码中在更具体的实现中处理它。

如果我使用泛型并制作 T 类型的有效负载,我的其他类也必须包含 T 类型。例如,在我的一个名为 Commander 的高级特征中,我有一个名为 handleSuccess 的方法,它接收 SuccessMessage ,并且它必须能够接收泛型 T 的成功消息,迫使我的指挥官也属于泛型 T。

我不确定我是否必须在这两个较小的邪恶之间进行选择,或者是否有更好的方法来做到这一点。我必须包含一些生成有效负载的方法,因为成功消息需要不同类型的有效负载。

编辑: 泛型还有一个额外的问题。 Commander 必须能够处理多种不同类型的 SuccessMessage,因此如果在 handleSuccess 中使用 SuccessMessage[T],强制 Commander 成为 Commander[T],那么 Commander 将无法处理不同类型的成功消息(我想想,不是 100% 肯定)。

编辑 2: 这里有一点指挥官:

trait Commander {
  def handleSuccess(message: SuccessMessage){
     //based on SuccessMessage.ActorType, do different stuff
  }

class SuccessMessage(val actorType: ActorType, val payload: Option[Any])
class SuccessMessage(val actorType: ActorType, val payload: Option[T])

编辑 3:这是对工作流程的描述,以更好地解释问题。

  • Commander 向 Poller 发送消息
    • 轮询器向指挥官发送 SuccessMessage(self, List[String])
  • Commander 将 List[String] 发送到 Processor
    • 处理器向指挥官发送 SuccessMessage(self, File)
  • 指挥官向上传者发送文件
    • 上传者向指挥官发送 SuccessMessage(self, Boolean)
  • 等等。

因此,我们需要 SuccessMessage 采取不同的类型。

【问题讨论】:

  • 如果您向我们展示一些代码来帮助您,那就更好了。但是,是的,如果类在整个项目中被广泛使用,那么用泛型改造现有类可能会产生巨大的设计负担......这就是为什么在开始编写之前最好拥有一个开发良好的领域对象模型的原因。跨度>
  • 具体来说,handleSuccess 对成功消息的有效负载值做了什么?
  • 您的“更具体的实现”子类是继承自 SuccessMessage 还是只是使用它的类?
  • 代码很多,所以不想全部粘贴。本质上,我使用的是 akka Actor 系统,因此指挥官创建不同的 Actor,向它发送消息以执行某项操作,然后 Actor 会将 SuccessMessages 返回给指挥官。这些 SuccessMessage 中的有效负载属于不同类型。
  • 进行编辑以添加代码示例。

标签: scala generics casting


【解决方案1】:

除非T 有一些类型界限允许编译器对其行为进行假设,否则您必须将 T 的实例强制转换为某个具体类型才能将它们用于任何明确的目的。 (与Any基本相同的问题)

您可能想查看类型类。然后,您可以为每种类型定义某种处理程序,并使用隐式类型类实例将传入的有效负载与处理程序匹配。 (见https://github.com/mpilquist/simulacrum库)

编辑:此处的示例 -> https://gist.github.com/phaistos/51f0405a2f25812a5780 基本上你最终会得到一个类型类:

@typeclass trait CommanderHandler[A] {
   def handle(payload: A): Unit
}

然后为每种类型创建一个实例,用作有效负载:

implicit val handlerInt: CommanderHandler[Int] = new CommanderHandler[Int] {
  def handle(payload: Int) { /* use the payload here */ }
}

然后你可以创建一个这样的函数来处理它们:

def consumePayload[T](t: T)(implicit h: CommanderHandler[T]) {
  h.handle(t)
}

您为给定类型创建的隐式必须在consumePayload 的调用站点上可用。基本上,您只是将每种类型的处理程序包装在一个漂亮的小包中。

这是迄今为止我为此类事情找到的最简洁的模式。希望能帮助到你。如果您使用 simulacrum,请确保您阅读了文档,您需要在创建类型类后进行一些导入以使其工作。

编辑:作为替代方案,您还可以通过 shapeless 查看多态方法:https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#polymorphic-function-values

【讨论】:

  • “除非 T 有一些类型界限允许编译器对其行为假设某些事情,否则您将不得不强制转换”是作为一般陈述还是特定于问题?
  • 对于第一种情况,我想补充一点,没有类型边界的 T 仍然比 Any 更好,因为它存储类型信息并有助于避免强制转换,例如在集合或模型对象中。
  • 你不能调用一个方法或访问一个 T 上的一个字段,所以泛型方法体不能在没有强制转换或绑定的情况下对其做任何事情。
  • 或者我想你可以使用反射和运行时惩罚。无论如何,类型类模式都允许您在任何类型上强加运行时行为,而无需修改这些类型,也无需反射。
  • 我的意思是澄清 T 没有类型界限并不等同于 Any 在所有情况下。如果你有例如List[Int],您可以从列表中检索元素,而无需强制转换为 Int。如果它是 Any 列表,则必须强制转换。
猜你喜欢
  • 1970-01-01
  • 2023-03-12
  • 2021-02-21
  • 2017-08-21
  • 1970-01-01
  • 2015-04-09
  • 2010-11-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多