jstnchng
jstnchng

Reputation: 2221

Scala Generics vs Any

In the code I'm working on I have a class called SuccessMessage with fields actorType (an enum) and payload. I am not sure what type to make payload.

If I make payload of type Any, I have to do a lot of gross looking instanceOf type casting to handle it later on in my code in more specific implementations.

If I use generics, and make payload of type T, my other classes have to also include the type T. For example, in a high level trait of mine called Commander, I have a method called handleSuccess that takes in a SuccessMessage, and it will have to be able to take in a success message of generic type T, forcing my commander to be of generic type T too.

I am not sure if I have to pick between these two lesser evils, or if there is a better way of doing it. I have to include some way of generifying payload, because Success Message requires a payload of different types.

EDIT: There is an additional problem with generics. Commander has to be able to handle many different kinds of SuccessMessages, so if it takes a SuccessMessage[T] in handleSuccess, forcing Commander to be Commander[T], then Commander won't be able to handle success messages with different types (I think, not 100% sure).

EDIT 2: Here is a short bit of 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: Here is a description of the workflow to better explain the problem.

Therefore, we need SuccessMessage to take different types.

Upvotes: 3

Views: 280

Answers (1)

Rich Henry
Rich Henry

Reputation: 1849

Unless T has some type bounds that allow the compiler to assume certain things about its behavior, you will have to cast instances of T to some concrete type to use them for any explicit purpose. (Basically same issue as Any)

You might want to look at type-classes. Then you can define some kind of handler for each type, and match the incoming payload to the handler using an implicit type-class instance. (see https://github.com/mpilquist/simulacrum library)

EDIT: Example here -> https://gist.github.com/phaistos/51f0405a2f25812a5780 Basically you end up with a typeclass like:

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

And then you make an instance for each type you want to use as a payload:

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

And then you can make a function like this to handle them:

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

The implicit you create for the given type must be available at the call site for consumePayload. Basically you're just wrapping the handler for each type in a nice little bundle.

This is the neatest pattern ive found for such things so far. Hope it helps. Make sure you read the docs for simulacrum if you use it, you will need to do some imports after creating the typeclass to make it work.

EDIT: As an alternative you could also look at polymorphic methods via shapeless: https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#polymorphic-function-values

Upvotes: 2

Related Questions