问题描述
好的,在关于'类变量作为常量'的问题,我知道常量在官方"构造函数运行之后(即直到你有一个实例)才可用.但是,如果我需要同伴单例来调用类怎么办:
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
}
如果我先创建伴生对象,'new thing(x)'(在伴生中)会导致错误.但是,如果我先定义类,x * someConst"(在类定义中)会导致错误.
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
}
}
然而,这样做给了我一个 'thing.thing' 类型的对象
However, doing this gives me a 'thing.thing' type object
val t = thing(2)
结果
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)
好的,'t1' 仍然具有 'thing.privThing' 的类型,但它现在可以被视为一个 '东西'.
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?
附注.我应该提到,我在 Windows 7 上使用 Scala 2.8.1
PS. I should mention, I'm using Scala 2.8.1 on Windows 7
推荐答案
首先,您看到的错误(您没有告诉我它是什么)不是运行时错误.thing
构造函数在 thing
单例初始化时不会被调用——稍后在调用 thing.apply
时会调用它,所以没有循环运行时引用.
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 the thing
singleton is initialized -- it's called later when you call thing.apply
, so there's no circular reference at runtime.
其次,您在编译时确实有一个循环引用,但是当您编译保存在磁盘上的 Scala 文件时,这不会导致问题——编译器甚至可以解决不同文件之间的循环引用.(我测试过.我把你的原始代码放在一个文件中并编译它,它工作正常.)
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.)
您真正的问题来自尝试在 Scala REPL 中运行此代码.这是 REPL 的作用以及为什么这是 REPL 中的问题. 您正在输入 object thing
,一旦您完成,REPL 就会尝试编译它,因为它到达了一个连贯的代码块的末尾.(分号推断能够推断出对象末尾的分号,这意味着编译器可以开始处理那块代码.)但是由于您还没有定义 class thing
它可以不编译它.当你颠倒 class thing
和 object thing
的定义时,你会遇到同样的问题.
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 defined class thing
it can't compile it. You have the same problem when you reverse the definitions of class thing
and object thing
.
解决方案是将 class thing
和 object thing
嵌套在某个外部对象中.这将推迟编译,直到外部对象完成,此时编译器将同时看到 class thing
和 object thing
的定义.您可以在此之后立即运行 import thingwrapper._
以使 class thing
和 object thing
在 REPL 的全局范围内可用.当您准备好将代码集成到某个文件中时,只需丢弃外部类 thingwrapper
.
The solution is to nest both class thing
and object thing
inside some outer object. This will defer compilation until that outer object is complete, at which point the compiler will see the definitions of class thing
and object thing
at the same time. You can run import thingwrapper._
right after that to make class thing
and object thing
available in global scope for the REPL. When you're ready to integrate your code into a file somewhere, just ditch the outer class 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
}
}
这篇关于Scala 单例工厂和类常量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!