Reputation: 573
I have this classes
public abstract class Foo {
def execute: Unit = ???
}
public abstract class Bar {
def execute: Unit = ???
}
public class FooFoo extends Foo {
def execute: Unit = ???
}
public class BarBar extends Bar {
def execute: Unit = ???
}
In some where I have this method:
def executeSomething(body: => AnyRef) : = Try(body) match ...
And the calls look like this
x match {
case _: Foo => executeSomething(x.execute)
case _: Bar => executeSomething(x.execute)
}
Is there are some way then I can do that like this (without new classes )
val u = executeSomething(x)
?
upd
sorry, guys. This m.b. real code will be more clear
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest
import com.google.api.client.googleapis.json.GoogleJsonResponseException
import com.google.api.services.analytics.Analytics
import com.google.api.services.analyticsreporting.v4.AnalyticsReporting
import com.my.lab.messages.{GRequest, GResponse}
import scala.util.{Failure, Success, Try}
class GQueueTask(req: GRequest, ref: ActorRef) extends Actor with ActorLogging {
def receive: Receive = {
case _ =>
}
def execute(body: => AnyRef): Unit = {
Try(body) match {
case Success(r) => ref ! GResponse(req, response = Option(r))
case Failure(f: GoogleJsonResponseException) =>
f.printStackTrace()
ref ! GResponse(req, error = Option(f))
case _ => ref ! GResponse(req, Option("unknown error"))
}
}
req match {
case GRequest(request, _) => request match {
case x: AnalyticsReporting#Reports#BatchGet => execute(x.execute()) // AbstractGoogleClientRequest
case x: Analytics#Data#Ga#Get => execute(x.execute()) // AbstractGoogleClientRequest
case x: Analytics#Management#Accounts#List => execute(x.execute()) // AbstractGoogleClientRequest
case x: Analytics#Management#Webproperties#List => execute(x.execute()) // AbstractGoogleClientRequest
case x: Analytics#Management#Profiles#List => execute(x.execute()) // AbstractGoogleClientRequest
case x: AuthorizationCodeTokenRequest => execute(x.execute()) // TokenRequest
case _ => ref ! GResponse(req)
}
}
context stop self
}
I tried to simplify "request match" block
Upvotes: 2
Views: 328
Reputation: 13985
This looks like a pretty good use-case for duck-typing
.
Duck Typing
is more formally referred as Structural Types
.
A Duck Type
can be thought of as just a specification
of desired set of properties. Compile time reflection
is used to ensure that the type
of provided parameter is compatible with the specification
of your duck-type
. So it provides you compile-time type-safety.
And then it uses run time reflection
to actually call the respective methods on provided instance.
So,
Lets say you have following abstractions,
abstract class Foo {
def execute(): Unit
}
abstract class Bar {
def execute(): Unit
}
class FooFoo extends Foo {
override def execute(): Unit = {
println("FooFoo")
}
}
class BarBar extends Bar {
override def execute(): Unit = {
println("BarBar")
}
}
Note :: I have added ()
to all your Unit
methods. This is a convention in Scala that all methods with side-effects should have ()
. And a Unit
method can not do anything other than side effects.
Now... you just need to define a duck-type
for the shared behaviour,
type HasExecuteMethod = {
def execute(): Unit
}
Now... lets define a function which uses this duck-type,
def doExecute(hasExecuteMethod: HasExecuteMethod): Unit = {
hasExecuteMethod.execute()
}
And... here you have it. This doExecute
method will accept instance of any class which has a def execute(): Unit
member.
Upvotes: 3
Reputation: 1594
Although you can achieve it by using structural typing (duck typing), it has a lot of overhead since scala use introspection at runtime. I think The best way to achieve it is by using type classes. It's in use in Scala all over the place :) Scala's Ordering is one example.
A type class defines functionality associated with "unrelated" types. What you need to do is define the type class in the form of a trait:
trait Executable[T] {
def execute(t: T): Unit
}
Than you need to make your types a member of that type class, for example:
implicit object FooFooExecutable extends Executable[FooFoo] {
override def execute(t: FooFoo): Unit = t.execute()
}
And the last thing is to define the method that takes your type class as an argument.
def exec[T: Executable](t: T): Unit = {
implicitly[ExecTypeClass[T]].execute(t)
}
You can read more about type classes here, here and here
Upvotes: 6
Reputation: 1032
I tried this and it worked:
object Dummy {
abstract class Foo {
def execute: Unit
}
abstract class Bar {
def execute: Unit
}
class FooFoo extends Foo {
def execute: Unit = { println("Foo") }
}
class BarBar extends Bar {
def execute: Unit = { println("Bar") }
}
def run(x: AnyRef): Unit = {
x match {
case f: Foo => f.execute
case b: Bar => b.execute
case _ =>
}
}
def main(args: Array[String]): Unit = {
val any = new BarBar()
run(any)
}
}
It printed
Bar
Upvotes: 1