Scala泛型 vs Any

3
在我正在处理的代码中,我有一个名为SuccessMessage的类,其中包含actorType(枚举)和payload两个字段。我不确定应该将payload设置为什么类型。
如果我将payload设置为Any类型,那么在稍后的代码中,我需要进行很多恶心的instanceOf类型转换才能处理它,以便更具体的实现。
如果我使用泛型,并将payload设置为T类型,我的其他类也必须包含类型T。例如,在我的高级Trait中,称为Commander,我有一个名为handleSuccess的方法,它接收一个SuccessMessage,并且它还必须能够接收T类型的成功消息,从而强制要求我的commander也是T类型。
我不确定是否必须在这两个较小的恶之间做出选择,或者是否有更好的方法。我必须包含某种通用payload的方式,因为SuccessMessage需要不同类型的payload。
编辑: 泛型还存在另一个问题。Commander必须能够处理许多不同类型的SuccessMessages,因此,如果在handleSuccess中使用了SuccessMessage[T],则会强制将Commander设置为Commander[T],那么Commander就无法处理具有不同类型的成功消息(我认为,不是100%确定)。
编辑2: 这是Commander的简短代码:
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])

EDIT 3: 这里提供了一个工作流程来更好地解释问题。
- Commander发送信息到Poller - Poller向Commander发送SuccessMessage(self, List[String])
- Commander将List[String]发送给Processor - Processor向Commander发送SuccessMessage(self, File)
- Commander将File发送给Uploader - Uploader向Commander发送SuccessMessage(self, Boolean)
- 以此类推
因此,我们需要让SuccessMessage接受不同的类型。

如果您展示一些代码将更有助于我们帮助您。但是,是的,如果该类在项目中广泛使用,则使用泛型来改进现有类可能会产生实质性的设计负担... 这就是为什么最好在开始编写之前拥有一个经过充分开发的领域对象模型的原因。 - scottb
handleSuccess 具体来说会如何处理成功消息的有效载荷值? - Dan Getz
你的“更具体的实现”是继承自SuccessMessage的子类,还是只是使用它的类? - sepp2k
这里有很多代码,所以我不想全部粘贴出来。基本上,我正在使用akka actor系统,所以指挥官创建了不同的演员,向它发送消息以执行某些操作,然后演员将成功消息返回给指挥官。这些成功消息中的有效载荷是不同类型的。 - jstnchng
已进行编辑以添加代码示例。 - jstnchng
显示剩余3条评论
1个回答

2
除非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,请确保阅读文档,您需要在创建typeclass后进行一些导入才能使其工作。
编辑:作为替代方案,您还可以查看通过shapeless实现的多态方法:https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#polymorphic-function-values

对于第一种情况,我想补充说明,没有类型边界的 T 仍然比 Any 更好,因为它存储了类型信息,并有助于避免在集合或模型对象中进行强制转换。 - User
或者你可以使用反射,但会有运行时的惩罚。无论如何,类型类模式允许您在不修改这些类型且不使用反射的情况下对任何类型施加运行时行为。 - Rich Henry
我的意思是要澄清没有类型边界的 T 并不在所有情况下等同于 Any。如果你有一个 List[Int],你可以从列表中检索元素而无需强制转换为 Int。如果它是任何类型的列表,你就必须进行强制转换。 - User
OP的“Commander”类有意地没有使用“T”——它接受各种类型的“SuccessMessage”。如果他只处理一种类型的“SuccessMessage”,并将其专门化为某种类型的“T”,那么是的,他不需要强制转换——但这与他的示例无关。 - Rich Henry
这里是一个要解决的问题以及如何通过类型类来解决它的Gist示例->https://gist.github.com/phaistos/51f0405a2f25812a5780 - Rich Henry
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接