Lars Tackmann
Lars Tackmann

Reputation: 20865

Using Dynamic to invoke overloaded method

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

Answers (2)

Daniel C. Sobral
Daniel C. Sobral

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

Paolo Falabella
Paolo Falabella

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

Related Questions