从宏覆盖方法
Override method from macro
是否可以从宏调用中添加方法定义?这是一个例子:
object Macros { def someMacro = ...}
trait Foo { def foo:Int }
class Bar extends Foo { someMacro() }
//calls to (new Bar).foo is possible and return value defined in someMacro
不能用宏覆盖抽象方法。但是,您可以覆盖具体方法,即使具体方法本身覆盖了抽象方法。因此,如果您引入具有默认实现的中间特征,您可以获得所需的结果。例如:
object Macros {
def someMacro(c: Context): c.Expr[Int] = {
import c.universe._
c.Expr[Int](Literal(Constant(5)))
}
}
trait Foo {
def foo: Int
}
trait FooWithDefault extends Foo {
override def foo: Int = 0
}
class Bar extends FooWithDefault {
override def foo: Int = macro Macros.someMacro
}
(new Bar).foo //5
这确实会导致一些意外行为,所以要小心:
val b: Foo = new Bar
b.foo //java.lang.AbstractMethodError
val b: FooWithDefault = new Bar
b.foo //java.lang.AbstractMethodError
这在问题 SI-7657 中有更详细的解释,解释如下:
Currently we allow macros to override non-abstract methods (in order
to provide performance enhancements such as foreach
for
collections), and we also disallow macros to override abstract methods
(otherwise downcasting might lead to AbstractMethodErrors
).
This patch fixes an oversight in the disallowing rule that prohibited
macros from overriding a concrete method if that concrete method
itself overrides an abstract method. RefCheck
entertains all
overriding pairs, not only the immediate ones, so the disallowing rule
was triggered.
Now macros can override abstract methods if and only if
either the base type or the self type contain a matching non-abstract
method.
这确实可以通过 Scala 宏注释实现。宏注释启用的一件事是向 class 定义添加成员。由于宏是在其他类型检查步骤之前编译的,因此您可以使用此功能来满足特征的约束。
例如,下面的宏注解@foo
在其注解的classes中添加了一个foo
方法。在这里,该方法没有参数,并且 returns 一个 Int
:
object Macros {
def fooImpl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val fooMethod = DefDef(NoMods, newTermName("foo"), List(), List(List()), TypeTree(), Literal(Constant(5)))
c.Expr[Any](annottees.map(_.tree).toList match {
case List(ClassDef(mods, name, tdefs, Template(parents, self, body))) =>
ClassDef(mods, name, tdefs, Template(parents, self, body :+ fooMethod))
})
}
}
class foo extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro Macros.fooImpl
}
然后你可以使用@foo
注解来满足一些Trait
定义:
trait Foo { def foo: Int}
@foo
class Bar extends Foo //compiles
(new Bar).foo //5
您可以阅读有关宏注释的更多信息here。
是否可以从宏调用中添加方法定义?这是一个例子:
object Macros { def someMacro = ...}
trait Foo { def foo:Int }
class Bar extends Foo { someMacro() }
//calls to (new Bar).foo is possible and return value defined in someMacro
不能用宏覆盖抽象方法。但是,您可以覆盖具体方法,即使具体方法本身覆盖了抽象方法。因此,如果您引入具有默认实现的中间特征,您可以获得所需的结果。例如:
object Macros {
def someMacro(c: Context): c.Expr[Int] = {
import c.universe._
c.Expr[Int](Literal(Constant(5)))
}
}
trait Foo {
def foo: Int
}
trait FooWithDefault extends Foo {
override def foo: Int = 0
}
class Bar extends FooWithDefault {
override def foo: Int = macro Macros.someMacro
}
(new Bar).foo //5
这确实会导致一些意外行为,所以要小心:
val b: Foo = new Bar
b.foo //java.lang.AbstractMethodError
val b: FooWithDefault = new Bar
b.foo //java.lang.AbstractMethodError
这在问题 SI-7657 中有更详细的解释,解释如下:
Currently we allow macros to override non-abstract methods (in order to provide performance enhancements such as
foreach
for collections), and we also disallow macros to override abstract methods (otherwise downcasting might lead toAbstractMethodErrors
).This patch fixes an oversight in the disallowing rule that prohibited macros from overriding a concrete method if that concrete method itself overrides an abstract method.
RefCheck
entertains all overriding pairs, not only the immediate ones, so the disallowing rule was triggered.Now macros can override abstract methods if and only if either the base type or the self type contain a matching non-abstract method.
这确实可以通过 Scala 宏注释实现。宏注释启用的一件事是向 class 定义添加成员。由于宏是在其他类型检查步骤之前编译的,因此您可以使用此功能来满足特征的约束。
例如,下面的宏注解@foo
在其注解的classes中添加了一个foo
方法。在这里,该方法没有参数,并且 returns 一个 Int
:
object Macros {
def fooImpl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val fooMethod = DefDef(NoMods, newTermName("foo"), List(), List(List()), TypeTree(), Literal(Constant(5)))
c.Expr[Any](annottees.map(_.tree).toList match {
case List(ClassDef(mods, name, tdefs, Template(parents, self, body))) =>
ClassDef(mods, name, tdefs, Template(parents, self, body :+ fooMethod))
})
}
}
class foo extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro Macros.fooImpl
}
然后你可以使用@foo
注解来满足一些Trait
定义:
trait Foo { def foo: Int}
@foo
class Bar extends Foo //compiles
(new Bar).foo //5
您可以阅读有关宏注释的更多信息here。