How to implicitly invoke a method when other metho

2019-08-14 17:28发布

问题:

I have a class which defines a private maintenance method synch, which I want to always be invoked whenever any other method of the class is invoked. The classic way of doing this would of course be:

def method1 = {
    synch
    // ... do stuff
}

def method2 = {
    synch
    // ... do other stuff
}

However, is there any way to have this done implicitly, so that I do not have to invoke it explicitly like I do above?

EDIT:

If it is possible to do this, is it also possible to define if I want the synch method to be called after or before each other method?

回答1:

You could create your custom wrapper using def macros and Dynamic like this:

import scala.reflect.macros.Context
import scala.language.experimental.macros

def applyDynamicImplementation(c: Context)(name: c.Expr[String])(args: c.Expr[Any]*) : c.Expr[Any] = {
  import c.universe._

  val nameStr = name match { case c.Expr(Literal(Constant(s: String))) => s }
  if (nameStr != "sync")
    c.Expr[Any](q"""{
      val res = ${c.prefix}.t.${newTermName(nameStr)}(..$args)
      ${c.prefix}.t.sync
      res
    }""")
  else
    c.Expr[Any](q"""${c.prefix}.t.sync""")
}

import scala.language.dynamics

class SyncWrapper[T <: { def sync(): Unit }](val t: T) extends Dynamic {
  def applyDynamic(name: String)(args: Any*): Any = macro applyDynamicImplementation
}

You'll have to use a compiler plugin for quasiquotes. If you want to call sync before method - just switch val res = ... and ${c.prefix}.t.sync lines.

Usage:

class TestWithSync {
  def test(a: String, b: String) = {println("test"); a + b}
  def test2(s: String) = {println("test2"); s}
  def sync() = println("sync")
}

val w = new SyncWrapper(new TestWithSync)

scala> w.test("a", "b")
test
sync
res0: String = ab

scala> w.test2("s")
test2
sync
res1: String = s

scala> w.invalidTest("a", "b")
<console>:2: error: value invalidTest is not a member of TestWithSync
              w.invalidTest("a", "b")
                           ^


回答2:

You could do it with bytecode rewriting, or with macro annotations. Both would be quite complicated. Some things to consider:

  1. Do you also want this to happen to inherited methods?

  2. If synch calls any other methods of this class, you will get an infinite loop.



回答3:

Implicit definitions are those that the compiler is allowed to insert into a program in order to fix any of its type errors. For example, if x + y does not type check, then the compiler might change it to convert(x) + y, where convert is some available implicit conversion. If convert changes x into something that has a + method, then this change might fix a program so that it type checks and runs correctly. If convert really is just a simple conversion function, then leaving it out of the source code can be a clarification.

Basically implicit is used for conversion implicitly so i tried it using an example i think it will help you:

scala> class c{
     |  implicit def synch(x: String)=x.toInt+20
     | def f1(s: String):Int=s
     | }

what am i doing is :

i am implicitely conversing String to int and adding 20 to that number so for that i defined one method that is synch by using keyword implicit that will take string as an argument and after that convert value to int and adding 20 to it.

in next method if arugment is string and it return type is Int so it will implicitely call that synch method

scala> val aty=new c
aty: c = c@1f4da09

scala> aty.f1("50")
res10: Int = 70


标签: scala