我想在宏中使用私有构造函数。此示例是一个正整数,但基本模式不仅可以用于其他数字类型(如偶数),还可以用于字符串派生类型(如电子邮件地址或目录名称)。通过使构造函数为私有,用户被拒绝制作非法类型的机会。我有以下代码:
object PosInt
{
import language.experimental.macros
import reflect.runtime.universe._
import reflect.macros.Context
def op(inp: Int): Option[PosInt] = if (inp > 0) Some(new PosInt(inp)) else None
def apply(param: Int): PosInt = macro apply_impl
def apply_impl(c: Context)(param: c.Expr[Int]): c.Expr[PosInt] =
{
import c.universe._
param match {
case Expr(Literal(i)) if (i.value.asInstanceOf[Int] > 0) =>
case Expr(Literal(i)) if (i.value.asInstanceOf[Int] == 0) => c.abort(c.enclosingPosition, "0 is not a positive integer")
case Expr(Literal(i)) => c.abort(c.enclosingPosition, "is not a positive integer")
case _ => c.abort(c.enclosingPosition, "Not a Literal")
}
reify{new PosInt(param.splice)}
}
}
class PosInt (val value: Int) extends AnyVal
但是,如果我将PosInt构造函数设为私有,虽然宏按预期编译,但如果尝试使用宏,则会出错。我无法弄清楚如何手动构建表达式树,但我不确定这是否会有所帮助。无论如何我能做到吗?
即使PosInt不是值类,您仍然无法使用私有构造函数。 我会接受一个不使用值类的答案。值类的缺点是它们会被类型擦除。我喜欢的类,如2d坐标的子集,无论如何都不能实现为值类。我实际上对正整数不感兴趣,我只是将它们用作一个简单的试验台。我正在使用Scala 2.11M5。 Scala 2.11将增加quasiquotes功能。我还没有弄清楚如何使用quasiquotes,因为目前它们上面的所有材料似乎都是我对Macro Paradise的熟悉,我没有。
答案 0 :(得分:2)
不幸的是,对于您要实现的目标,宏不会以这种方式工作。他们只是在编译时操作AST。无论最终结果是什么,总是你可以在Scala中逐字编写(没有宏)。
因此,为了约束PosInt
的可能值,您需要在公共构造函数或协同对象上的工厂方法中的某处进行运行时检查。
如果运行时异常对您不合适,那么可能的一种方法是:
create
方法,该方法返回Option[PosInt]
(或Try[PosInt]
,或您选择的其他类型,以表示“失败”当参数超出范围时)。apply
方法,在编译时验证参数是否在范围内然后返回一个只调用{{1}的表达式树}}。在这种情况下,在选项上调用create(x).get
是可以接受的,因为您确信它永远不会是.get
。
缺点是您必须重复检查两次:一次是在编译时,一次是在运行时。
答案 1 :(得分:0)
我不是专家,但我想我会试一试...... 在Java中,私有构造函数的范围仅限于同一个类......因此需要将PosInt对象移动到调用它的同一个类的范围内。 话虽如此,我发现了一篇文章,展示了两种保持对象不被继承的方式@ http://www.developer.com/java/other/article.php/3109251/Stopping-Your-Class-from-Being-Inherited-in-Java-the-Official-Way-and-the-Unofficial-Way.htm
它描述了使用" final"类声明中的关键字,以防止它被继承。那是"官员"办法。 "非正式的"方法是使构造函数私有,但添加一个返回类的对象的公共静态方法...
是的,我知道,这是一个老问题......但是没有答案。你永远不知道什么时候老问题会成为某人搜索结果中最受欢迎的......