好的,在关于'Class Variables as constants'的问题中,我得到的事实是,在“官方”构造函数运行之后(即直到你有一个实例),常量才可用。但是,如果我需要伴侣单身人士在课堂上进行调用该怎么办:
object thing {
val someConst = 42
def apply(x: Int) = new thing(x)
}
class thing(x: Int) {
import thing.someConst
val field = x * someConst
override def toString = "val: " + field
}
如果我先创建伴随对象,那么'new thing(x)'(在伴侣中)会导致错误。但是,如果我首先定义类,'x * someConst'(在类定义中)会导致错误。
我也尝试将类定义放在单例中。
object thing {
var someConst = 42
def apply(x: Int) = new thing(x)
class thing(x: Int) {
val field = x * someConst
override def toString = "val: " + field
}
}
然而,这样做会给我一个'thing.thing'类型的对象
val t = thing(2)
结果
t: thing.thing = val: 84
我提出的唯一有用的解决方案是创建一个抽象类,一个伴侣和一个内部类(扩展抽象类):
abstract class thing
object thing {
val someConst = 42
def apply(x: Int) = new privThing(x)
class privThing(x: Int) extends thing {
val field = x * someConst
override def toString = "val: " + field
}
}
val t1 = thing(2)
val tArr: Array[thing] = Array(t1)
好的,'t1'仍然具有'thing.privThing'类型,但它现在可以被视为'事物'。
然而,它仍然不是一个优雅的解决方案,谁能告诉我更好的方法呢?
PS。我应该提一下,我在Windows 7上使用Scala 2.8.1
答案 0 :(得分:12)
首先,您看到的错误(您没有告诉我它是什么)不是运行时错误。 thing
单例初始化时不调用thing
构造函数 - 稍后在调用thing.apply
时调用它,因此在运行时没有循环引用。
其次,你在编译时有一个循环引用,但是当你编译一个已保存在磁盘上的scala文件时,这不会导致问题 - 编译器甚至可以解析不同文件之间的循环引用。 (我测试过。我把你原来的代码放在一个文件中并编译它,它运行正常。)
您真正的问题来自于尝试在Scala REPL中运行此代码。 以下是REPL的作用以及为什么这是REPL中的一个问题。您正在输入object thing
,一旦完成,REPL会尝试编译它,因为它已到达终点一段连贯的代码。 (分号推断能够在对象的末尾推断出分号,这意味着编译器可以开始处理那段代码。)但是因为你没有定义class thing
它无法编译它。当您颠倒class thing
和object thing
的定义时,您会遇到同样的问题。
解决方案是将class thing
和object thing
嵌套在某个外部对象中。这将推迟编译,直到外部对象完成,此时编译器将同时看到class thing
和object thing
的定义。您可以在此之后立即运行import thingwrapper._
,以使class thing
和object thing
在REPL的全局范围内可用。 当您准备将代码集成到某个文件中时,只需抛弃外部类thingwrapper
。
object thingwrapper{
//you only need a wrapper object in the REPL
object thing {
val someConst = 42
def apply(x: Int) = new thing(x)
}
class thing(x: Int) {
import thing.someConst
val field = x * someConst
override def toString = "val: " + field
}
}
答案 1 :(得分:0)
Scala 2.12或更高版本可能会受益于sip 23,它只是(2016年8月)传递到下一次迭代(被认为是“好主意”,但是是一个在制品)
基于文字的单身人士类型
Singleton类型弥合了价值水平和类型水平之间的差距,因此允许在Scala中探索通常仅在支持全谱依赖类型的语言中可用的技术。
Scala的类型系统可以模拟常量(例如
42
,"foo"
,classOf[String]
)。
这些是在object O { final val x = 42 }
之类的情况下推断出来的。它们用于表示和传播编译时常量(请参阅6.24 Constant Expressions中的4.1 Value Declarations and Definitions和“常量值定义”的讨论。) 但是,没有表达这种类型的表面语法。这使得需要它们的人可以创建可以提供解决方法的宏(例如 shapeless )。
这可以通过相对简单的方式进行更改,因为启用此功能的整个机制已经存在于scala编译器中。
type _42 = 42.type
type Unt = ().type
type _1 = 1 // .type is optional for literals
final val x = 1
type one = x.type // … but mandatory for identifiers