With new method Symbol.newClass
(Scala 3.1.3) this becomes quite easy:
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]
}
}
Usage:
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") // override
Blog post: Possibility to generate arbitrary class implementations in macros
Scaladoc: Symbol.newClass
Issue: Support creating a new instance of a given Type[A] with Scala 3 macro
How to access constructor:
- How to create an instance of a generic type in a Scala 3 macro?
- How to access parameter list of case class in a dotty macro
- How do I use macro to create objects of any class
(Apply(Select.unique(New(TypeTree.of[T]), "<init>")...
)
Scala 2 Append A Method To Class Body (Metaprogramming) (Scala 2, compiler plugin)