我试图用Scala 3宏和好吃来覆盖一个方法。我想重写任何类型的方法。现在我从这个简单的案例开始。
我有一个测试基类:
class TestClass {
def func(s: String) = "base"
}我想要做到这一点,但通过使用好吃的方法,我发现不可能在带有引号和剪接的泛型类型上调用new A:
'{
new TestClass() {
override def func(s: String) = "override"
}
}.asExprOf[A]我打印了上述代码的AST,并几乎重新创建了它。问题是我不能在生成的类上调用new --我看不到访问新类的符号或类型的方法。我还尝试了使用新名称的Symbol.requiredClass(),虽然它返回了一些符号,但在宏展开过程中出现了一个错误,没有找到该类。
我的问题是:
new Class {}引号的情况下,可以派生自定义类型吗?ClassDef.copy是否注册了一个可以帮助创建新实例的新名称?ClassDef的手动调用可以创建类的实例吗?Symbol.requiredClass返回的符号,因为它返回一些东西,即使以前没有定义?我创建的代码:
import scala.quoted.*
object NewClass {
def newClassImpl[A: Type](e: Expr[A])(using Quotes): Expr[A] = {
import quotes.reflect.*
val typeRep = TypeRepr.of[A]
val ret = typeRep.classSymbol.map(_.tree) match {
case Some(
cd @ ClassDef(
name: String,
constr: DefDef,
parents: List[Tree],
selfOpt: Option[ValDef],
body: List[Statement]
)
) =>
println(cd.show(using Printer.TreeAnsiCode))
val newItemsOwner = Symbol.spliceOwner.owner
println("newItemsOwner = " + newItemsOwner)
def createFunction(args: Term)(using Quotes): Term = {
args
}
val newConstrSymbol = Symbol.newMethod(
newItemsOwner,
"<init>",
MethodType(Nil)(
_ => Nil,
_ => TypeRepr.of[Unit]
),
Flags.EmptyFlags,
Symbol.noSymbol
)
val newConstrDef: DefDef = DefDef(
newConstrSymbol,
{
case List(List(paramTerm: Term)) =>
Some(createFunction(paramTerm).changeOwner(newConstrSymbol))
case _ => None
}
)
val newMethodSymbol = Symbol.newMethod(
newItemsOwner,
"func",
MethodType(List("s"))(
_ => List(TypeRepr.of[String]),
_ => TypeRepr.of[String]
),
Flags.Override,
Symbol.noSymbol
)
val newMethodDef: DefDef = DefDef(
newMethodSymbol,
{
case List(List(paramTerm: Term)) =>
Some(createFunction(paramTerm).changeOwner(newMethodSymbol))
case _ => None
}
)
val parentSel = Select.unique(New(TypeTree.of[A]), "<init>")
val parent = Apply(parentSel, Nil)
val newClassDef: ClassDef = ClassDef.copy(cd)(
name + "$gen",
newConstrDef,
parent :: Nil,
None,
newMethodDef :: Nil
)
val app = Apply(
Select(New(TypeIdent(Symbol.requiredClass(name + "$gen"))), newConstrDef.symbol),
Nil
)
val block = Block(newClassDef :: Nil, Typed(app, TypeTree.of[A]))
val finalTerm = Inlined(Some(TypeTree.of[NewClass$]), Nil, block)
println(finalTerm.show(using Printer.TreeAnsiCode))
println(finalTerm.show(using Printer.TreeStructure))
finalTerm.asExprOf[A]
case other =>
println("No class def found: " + other)
e
}
println("Returned:")
println(ret.asTerm.show(using Printer.TreeAnsiCode))
println(ret.asTerm.show(using Printer.TreeStructure))
ret
}
inline def newClass[A](a: A): A = ${ newClassImpl[A]('{ a }) }
}返回的代码打印时没有任何抱怨,如下所示:
{
@scala.annotation.internal.SourceFile("src/main/scala/MethodsMain.scala") class TestClass$gen() extends TestClass {
override def func(s: java.lang.String): java.lang.String = s
}
(new TestClass$gen(): TestClass)
}但是,如果由宏返回,则在展开过程中得到一个错误:
[error] |Bad symbolic reference. A signature
[error] |refers to TestClass$gen/T in package <empty> which is not available.
[error] |It may be completely missing from the current classpath, or the version on
[error] |the classpath might be incompatible with the version used when compiling the signature.
[error] | This location contains code that was inlined from NewClass.scala:86用法:
val res:TestClass = NewClass.newClass[TestClass](new TestClass)谢谢你的帮助。
发布于 2022-09-18 22:01:18
使用新方法Symbol.newClass (Scala3.1.3),这变得非常容易:
import scala.annotation.experimental
import scala.quoted.*
object NewClass {
inline def newClass[A]: A = ${newClassImpl[A]}
@experimental
def newClassImpl[A: Type](using Quotes): Expr[A] = {
import quotes.reflect.*
val name: String = TypeRepr.of[A].typeSymbol.name + "Impl"
val parents = List(TypeTree.of[A])
def decls(cls: Symbol): List[Symbol] =
List(Symbol.newMethod(cls, "func", MethodType(List("s"))(_ => List(TypeRepr.of[String]), _ => TypeRepr.of[String]), Flags.Override, Symbol.noSymbol))
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
val funcSym = cls.declaredMethod("func").head
val funcDef = DefDef(funcSym, argss => Some('{"override"}.asTerm))
val clsDef = ClassDef(cls, parents, body = List(funcDef))
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[A])
Block(List(clsDef), newCls).asExprOf[A]
}
}用法:
class TestClass {
def func(s: String) = "base"
}
val res: TestClass = NewClass.newClass[TestClass]
//{
// class TestClassImpl extends TestClass {
// override def func(s: java.lang.String): java.lang.String = "override"
// }
//
// (new TestClassImpl(): TestClass)
//}
res.func("xxx") // overrideScaladoc:Symbol.newClass
发行:支持使用Scala 3宏创建给定Type[A]的新实例
如何访问构造函数:
Apply(Select.unique(New(TypeTree.of[T]), "<init>")...)Scala 2在类体中附加一个方法(元编程) (Scala 2,编译器插件)
https://stackoverflow.com/questions/68550985
复制相似问题