是否可以定义可以由类而不是对象实现的父类或特征?
trait OnlyForClasses {
// magic goes here
}
class Foo extends OnlyForClasses {
// this is OK
}
object Bar extends OnlyForClasses {
// INVALID, ideally fails a type check
}
答案 0 :(得分:3)
尝试使用macro annotation获取密封特征
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
@compileTimeOnly("enable macro paradise to expand macro annotations")
class checkNoObjectChild extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro checkNoObjectChildMacro.impl
}
object checkNoObjectChildMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: _
if mods.hasFlag(Flag.SEALED) =>
val checkRecursion = c.openMacros.count(check =>
(c.macroApplication.toString == check.macroApplication.toString) &&
(c.enclosingPosition.toString == check.enclosingPosition.toString)
)
if (checkRecursion > 2)
q"..$annottees"
else {
val tpe = c.typecheck(tq"$tpname", mode = c.TYPEmode, silent = true)
val objectChildren = tpe.symbol.asClass.knownDirectSubclasses.filter(_.isModuleClass)
if (objectChildren.isEmpty)
q"..$annottees"
else
c.abort(c.enclosingPosition, s"Trait $tpname has object children: $objectChildren")
}
case _ =>
c.abort(c.enclosingPosition, s"Not a sealed trait: $annottees")
}
}
}
@checkNoObjectChild
sealed trait OnlyForClasses {
}
class Foo extends OnlyForClasses {
// compiles
}
object Bar extends OnlyForClasses {
// doesn't compile
}
https://stackoverflow.com/a/20466423/5249621
How to debug a macro annotation?
或者尝试materializing类型类
trait NoObjectChild[T]
object NoObjectChild {
implicit def materialize[T]: NoObjectChild[T] = macro impl[T]
def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val tpe = weakTypeOf[T]
val cls = tpe.typeSymbol.asClass
if (!(cls.isTrait && cls.isSealed))
c.abort(c.enclosingPosition, s"$tpe is not a sealed trait")
val objectChildren = cls.knownDirectSubclasses.filter(_.isModuleClass)
if (objectChildren.isEmpty)
q"new NoObjectChild[$tpe] {}"
else
c.abort(c.enclosingPosition, s"Trait $tpe has object children: $objectChildren")
}
}
sealed trait OnlyForClasses {
}
object OnlyForClasses {
implicitly[NoObjectChild[OnlyForClasses]]
}
class Foo extends OnlyForClasses {
// compiles
}
object Bar extends OnlyForClasses {
// doesn't compile
}
答案 1 :(得分:1)
作为一个不完善的解决方案,您可以添加一个用户无法创建的构造函数参数,并要求在扩展类中传递它。这也将阻止他们创建实例。即您写(有些欺骗性来禁止null
):
class DummyArgument private[your_package] (val x: Int) extends AnyVal {}
class OnlyForClasses(da: DummyArgument)
用户可以写
class Foo(da: DummyArgument) extends OnlyForClasses(da) {
// this is OK
}
但不能写任何
object Bar extends OnlyForClasses(something) {
// this is OK
}
object Bar(da: DummyArgument) extends OnlyForClasses {
// this is OK
}
val foo = new Foo(something)