Reputation: 20865
In C# its possibly to use AsDynamic to invoke a overloaded method on a subclass (example here) thus allowing an abstract class to invoke methods it does not itself define. Is something similar possible with Scala 2.9 applyDynamic ?. I tried the following
abstract class AggregateRoot extends Dynamic {
def applyChange(event: Event) {
this.applyDynamic("handleEvent")(event)
}
// this does not work
def applyDynamic(name : String)(args: Any*) = this
}
used like this
class InventoryItem extends AggregateRoot {
def handleEvent(event: InventoryItemCreated) {
println("received InventoryItemCreated")
}
def handleEvent(event: InventoryItemDeactivated) {
println("received InventoryItemDeactivated")
}
}
where InventoryItemCreated and InventoryItemDeactivated both are events
class Event;
class InventoryItemDeactivated extends Event;
class InventoryItemCreated extends Event;
I then expect to be able to do something like this
var aggregate : AggregateRoot = new InventoryItem
var event = new InventoryItemDeactivated
aggregate.applyChange(event) // should print "received InventoryItemDeactivated"
but I cannot figure out how to define applyDynamic (in AggregateRoot) so that it can invoke overloaded methods in subclasses at runtime without itself defining them. Other solutions that achieve the same result are welcome (perhaps structural typing can come in handy ?).
Upvotes: 3
Views: 492
Reputation: 297265
Dynamic
doesn't gain you anything here because what it does it to let you define the mechanics for handling an undefined method. In your example, however, all methods called are defined.
What you actually want is the mechanics of finding a method defined in the class, and this does not exist, because Dynamic
means to be a bridge to other JVM languages, which may implement their "methods" in completely different manners.
All you need to do, however, is use reflection. Scala doesn't have, as of 2.9.1, a reflection library, but Java's good enough for the purpose. Here's how you'd write AggregateRoot
:
abstract class AggregateRoot {
def applyChange(event: Event) {
this.getClass.getMethod("handleEvent", event.getClass).invoke(this, event)
}
}
What Dynamic
would allow you to do is:
abstract class AggregateRoot extends Dynamic {
def applyDynamic(name : String)(args: Any*) =
this
.getClass
.getMethod(name, args map (_.getClass): _*)
.invoke(this, args map (_.asInstanceOf[Object]): _*)
}
And then do this at the end:
aggregate.handleEvent(event)
Where aggregate
, because it is of type AggregateRoot
and not InventoryItem
, doesn't know it has the method handleEvent
. But I suspect this is not what you want.
Upvotes: 3
Reputation: 25844
I don't fully understand what all the example code in your links is supposed to do (AsDynamic()
is not part of the standard BCL) but given a cursory look at what you've sent, I would say that scala's Dynamic trait will not help you.
.NET's dynamic
allows you (among other things) to have multiple dispatch in c#. If I'm not mistaken this is what your example is trying to do, calling the right handleEvent
method based on the runtime type of the event
argument.
Scala's Dynamic trait, instead, can not (AFAIK) used to do this: it was meant for when you need/want to call a method that does not exist at compile-time (which is one of the things .NET's dynamic allows you to do too, but unfortunately not what you seem to need in this case).
E.g. scala's Dynamic is for doing things like this:
scala> class Foo extends Dynamic {
| def applyDynamic(name: String)(args: Any*) = name
| }
defined class Foo
scala> new Foo
res0: Foo = Foo@17757ad
scala> res0.hello
dynatype: $line2.$read.$iw.$iw.res0.applyDynamic("hello")()
res1: String = hello
(since Foo
does not have a "hello
" method, but it extends the Dynamic
trait, scala's compiler replaces the call to hello
with a call to applyDynamic("hello")
)
Upvotes: 3