【问题标题】:Scala constant expressions and computed string literalsScala 常量表达式和计算字符串文字
【发布时间】:2013-11-25 04:05:03
【问题描述】:

Java 注释和它们的参数必须是“常量”的规定遇到了一些棘手的问题。 Scala 2.8 语言规范的 6.24 规定“常量表达式”是以下任何一种(强调我的):

  • 值类的文字,例如整数
  • 字符串文字
  • 使用 Predef.classOf (§12.5) 构造的类
  • 来自底层平台的枚举元素
  • 一个字面量数组,形式为 Array(c1, ..., cn),其中所有的 ci 都是它们自己 常量表达式
  • 由常量值定义 (§4.1) 定义的标识符。

现在,“字符串文字”似乎被定义为实际上只是一个“”或“”“”“”分隔的字符块,这是非常明确的。那么,我的问题是为什么给定

object MyObject {
  final val MY_CONSTANT1="foo"
  final val MY_CONSTANT2="bar" + "baz"
  final val MY_CONSTANT3="qux" + "quux" + "frobozz"
}

// ...

@MyAnnotation( ??? )
def Foo(): Unit {
...

@MyAnnotation 使用 MY_CONSTANT1 和 MY_CONSTANT2 而不是 MY_CONSTANT3 编译和 scaladocs(我得到“注释参数需要是常量”)。为什么 MY_CONSTANT2 完全有效?是否有一些未指定的最多两个字符串文字可以组合成一个更大的规则在工作,还是我疯了?

edit我使用的是 Scala 2.10,它似乎修复了早期 Scala 版本中一些与注释相关的编译器错误。

【问题讨论】:

  • 请关闭问题 - 它适用于 scala 2.10+
  • 如果它是“工作”(在这种情况下这意味着什么?),我不会遇到导致我提出问题的行为,所以不,我不会关闭问题.
  • 您能否详细说明如何重现此内容?这在 2.10.2 中对我有用: object MyObject { final val MY_CONSTANT1="foo" final val MY_CONSTANT2="bar" + "baz" final val MY_CONSTANT3="qux" + "quux" + "frobozz" } case class NoopAnnotation( val param: String) 扩展 scala.annotation.StaticAnnotation @NoopAnnotation("ABC") def abc1() = 1 @NoopAnnotation(MyObject.MY_CONSTANT1) def abc2() = 1 @NoopAnnotation(MyObject.MY_CONSTANT2) def abc3() = 1 @NoopAnnotation(MyObject.MY_CONSTANT3) def abc4() = 1
  • 我会尝试找一些时间 - 还没有发生。可能会在本周末发布有关该信息的更新。

标签: scala


【解决方案1】:

所有这些字符串都以相同的方式编译 - 它们都被折叠成单个文字。您可以在字节码中看到它们。

0: ldc           #19                 // String foo
0: ldc           #22                 // String barbaz
0: ldc           #24                 // String quxquuxfrobozz

对于 Matt Malone,您不会在 StaticAnnotation 中看到“注释参数必须是常量”,因为它不会强制执行类似的操作。这编译,没有警告。

class MyAnnotation(val s: String) extends scala.annotation.StaticAnnotation
object MyObject {
  def inconstant(): String = scala.util.Random.nextString(10)
  @MyAnnotation(s = inconstant) def f = null
}

诱导“注解参数必须是常量”的方法是首先扩展ClassfileAnnotation,然后做一些事情来阻止val具有常量类型。像这样的东西。类型归属导致 MY_CONSTANT1 具有类型 String 而不是常量类型 String("foo")。你不能直接在 scala 中表达常量类型,但它在内部是这样表示的。

class MyAnnotation(val s: String) extends scala.annotation.ClassfileAnnotation
object MyObject {
  final val MY_CONSTANT1: String = "foo" 
  final val MY_CONSTANT2 = "bar" + "baz"
  final val MY_CONSTANT3 = "qux" + "quux" + "frobozz"
}
object Bippy {
  @MyAnnotation(s = MyObject.MY_CONSTANT1) def f1 = null
  @MyAnnotation(s = MyObject.MY_CONSTANT2) def f2 = null
  @MyAnnotation(s = MyObject.MY_CONSTANT3) def f3 = null
}

/***
a.scala:8: error: annotation argument needs to be a constant; found: MyObject.MY_CONSTANT1
  @MyAnnotation(s = MyObject.MY_CONSTANT1) def f1 = null
                             ^
one warning found
one error found
***/

【讨论】:

  • 如果 My_CONSTANTX 是一个数组,我什至无法使用没有类型注释的 final val 使其工作:但是我能够通过直接传递表达式来使其工作.. . : @MyAnnotation(s = Array("someString"))
  • 对我上面的评论略有改进:final val someString = "asdf1234" 然后做@MyAnnotation(s = Array(someString))
【解决方案2】:

你说它“编译和 scaladocs”,所以我猜你的错误是当你运行 scaladoc 时,就像对我一样。

使用 scaladoc,您可以获得一个只运行到 typer 阶段的专用编译器。

它在打字机中自定义的一件事是:

override def canAdaptConstantTypeToLiteral = false

将其更改为 true,您的简单示例将 scaladoc。

adapt 开头的大评论说这是它做的第一件事,或者更确切地说,它在 scaladocking 时不做的第零件事。

*  (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)

只是为了好玩,我会尝试翻转旗帜,看看有什么坏处。 (编辑:scala 文档构建良好。该标志源于演示编译器行为,但对我来说它如何应用于 scaladoc 并不明显。)

【讨论】:

  • 哦,我以为是这样的,但不知道从哪里开始寻找它。
  • 在使用 SBT 生成 scaladoc 时如何更改此设置?
  • @AlessandroVermeulen 我认为没有标志。我一定接触过源代码,重新构建,并且在 build.sbt 中你可以指定你的本地 scala 发行版。我总是忘记怎么做。 scalaHome := Some(file("mydistro"))。这么多年了,我还是不喜欢 sbt。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-18
  • 2016-10-07
  • 1970-01-01
相关资源
最近更新 更多