17

我想在宏中使用私有构造函数。这个例子是一个正整数,但基本模式不仅可以用于其他数字类型,如偶数,还可以用于字符串派生类型,如电子邮件地址或目录名称。通过将构造函数设为私有,用户被拒绝创建非法类型的机会。我有以下代码:

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 功能。我还没有弄清楚如何使用准引号,因为目前它们上的所有材料似乎都假定我对 Macro Paradise 很熟悉,而我没有。

4

2 回答 2

2

不幸的是,对于您要实现的目标,宏不能以这种方式工作。他们只是在编译时操作 AST。无论最终结果是什么,它总是可以用 Scala 字面意思编写的(没有宏)。

因此,为了限制 的可能值PosInt,您将需要在某个地方进行运行时检查,无论是在公共构造函数中还是在伴随对象的工厂方法中。

如果您不喜欢运行时异常,那么一种可能的方法是:

  • 使构造函数在类上私有。
  • create在伴生对象上提供(例如)一个方法,该方法返回Option[PosInt](或Try[PosInt],或您选择的其他类型,允许您在参数超出范围时表达“失败”)。
  • 在伴随对象上提供一个apply类似于您的示例的方法,该方法在编译时验证参数是否在范围内,然后返回一个简单调用的表达式树create(x).get

在这种情况下调用.get选项是可以接受的,因为您确信它永远不会None

缺点是您必须重复检查两次:一次在编译时,一次在运行时。

于 2016-08-12T13:06:48.027 回答
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”关键字来防止它被继承。这就是“官方”的方式。“非官方”的方式是将构造函数设为私有,但添加一个返回类的对象的公共静态方法......

是的,我知道,这是一个古老的问题......但它没有得到回答。你永远不知道什么时候一个老问题会成为某人搜索结果中的热门话题......

于 2015-04-10T21:28:09.797 回答