Scala 单例工厂和类常量
好的,在关于“类变量作为常量”的问题中,我事实是,这些常量只有在“官方”构造函数运行之后才可用(即直到您有一个实例)。但是,如果我需要伴随单例来调用该类怎么办:
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”类型,但现在可以将其视为“事物”。
然而,这仍然不是一个优雅的解决方案,有人能告诉我更好的方法吗?
附言。我应该提到,我在 Windows 7 上使用 Scala 2.8.1
OK, in the question about 'Class Variables as constants', I get the fact that the constants are not available until after the 'official' constructor has been run (i.e. until you have an instance). BUT, what if I need the companion singleton to make calls on the class:
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
}
If I create companion object first, the 'new thing(x)' (in the companion) causes an error. However, if I define the class first, the 'x * someConst' (in the class definition) causes an error.
I also tried placing the class definition inside the singleton.
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
}
}
However, doing this gives me a 'thing.thing' type object
val t = thing(2)
results in
t: thing.thing = val: 84
The only useful solution I've come up with is to create an abstract class, a companion and an inner class (which extends the abstract class):
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)
OK, 't1' still has type of 'thing.privThing', but it can now be treated as a 'thing'.
However, it's still not an elegant solution, can anyone tell me a better way to do this?
PS. I should mention, I'm using Scala 2.8.1 on Windows 7
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,您看到的错误(您没有告诉我它是什么)不是运行时错误。当
thing
单例初始化时,不会调用thing
构造函数 - 它会在稍后调用thing.apply
时调用,因此不存在循环运行时参考。其次,在编译时确实存在循环引用,但是当您编译保存在磁盘上的 scala 文件时,这不会导致问题 - 编译器甚至可以解决不同文件之间的循环引用。 (我测试过。我把你的原始代码放在一个文件中并编译它,它工作得很好。)
你真正的问题来自于尝试在 Scala REPL 中运行这段代码。 这是 REPL 的作用以及为什么这是 REPL 中的问题。您正在输入
对象事物
,一旦完成,REPL 就会尝试编译它,因为它已经到达了一个连贯的代码块的末尾。 (分号推断能够推断出对象末尾的分号,这意味着编译器可以开始处理该代码块。)但是由于您还没有定义class thing
它可以不编译它。当你颠倒类事物
和对象事物
的定义时,你也会遇到同样的问题。解决方案是将
类事物
和对象事物
嵌套在某个外部对象内。这将推迟编译,直到外部对象完成,此时编译器将同时看到class thing
和object thing
的定义。您可以在此之后立即运行import thingwrapper._
以使class thing
和object thing
在 REPL 的全局范围内可用。 当您准备好将代码集成到某个文件中时,只需放弃外部类thingwrapper
即可。First, the error you're seeing (you didn't tell me what it is) isn't a runtime error. The
thing
constructor isn't called when thething
singleton is initialized -- it's called later when you callthing.apply
, so there's no circular reference at runtime.Second, you do have a circular reference at compile time, but that doesn't cause a problem when you're compiling a scala file that you've saved on disk -- the compiler can even resolve circular references between different files. (I tested. I put your original code in a file and compiled it, and it worked fine.)
Your real problem comes from trying to run this code in the Scala REPL. Here's what the REPL does and why this is a problem in the REPL. You're entering
object thing
and as soon as you finish, the REPL tries to compile it, because it's reached the end of a coherent chunk of code. (Semicolon inference was able to infer a semicolon at the end of the object, and that meant the compiler could get to work on that chunk of code.) But since you haven't definedclass thing
it can't compile it. You have the same problem when you reverse the definitions ofclass thing
andobject thing
.The solution is to nest both
class thing
andobject thing
inside some outer object. This will defer compilation until that outer object is complete, at which point the compiler will see the definitions ofclass thing
andobject thing
at the same time. You can runimport thingwrapper._
right after that to makeclass thing
andobject thing
available in global scope for the REPL. When you're ready to integrate your code into a file somewhere, just ditch the outer classthingwrapper
.Scala 2.12 或更高版本可能会受益于 sip 23< /a> 刚刚(2016 年 8 月)传递到下一个迭代(被认为是一个“好主意”,但仍在进行中)
Scala 2.12 or more could benefit for sip 23 which just (August 2016) pass to the next iteration (considered a “good idea”, but is a work-in-process)