Jea*_*let 4 scala final initialization variable-assignment
这是我之前的初始化变量问题的后续问题.
假设我们正在处理这种情况:
object AppProperties {
private var mgr: FileManager = _
def init(config: Config) = {
mgr = makeFileManager(config)
}
}
Run Code Online (Sandbox Code Playgroud)
此代码的问题在于AppProperties可能重新分配任何其他方法mgr.是否有一种技术可以更好地封装,mgr以便感觉像val其他方法一样?我想过这样的事情(灵感来自这个答案):
object AppProperties {
private object mgr {
private var isSet = false
private var mgr: FileManager = _
def apply() = if (!isSet) throw new IllegalStateException else mgr
def apply(m: FileManager) {
if (isSet) throw new IllegalStateException
else { isSet = true; mgr = m }
}
}
def init(config: Config) = {
mgr(makeFileManager(config))
}
}
Run Code Online (Sandbox Code Playgroud)
...但这对我来说感觉相当重要(初始化让我想起了太多的C++ :-)).还有其他想法吗?
您可以使用implicits进行此操作,使隐式仅在应该能够重新分配的方法中可用.查看该值不需要隐式,因此"变量"对其他方法可见:
sealed trait Access
trait Base {
object mgr {
private var i: Int = 0
def apply() = i
def :=(nv: Int)(implicit access: Access) = i = nv
}
val init = {
implicit val access = new Access {}
() => {
mgr := 5
}
}
}
object Main extends Base {
def main(args: Array[String]) {
println(mgr())
init()
println(mgr())
}
}
Run Code Online (Sandbox Code Playgroud)
好的,这是我的建议,直接受到axel22、Rex Kerr和Debilski答案的启发:
class SetOnce[T] {
private[this] var value: Option[T] = None
def isSet = value.isDefined
def ensureSet { if (value.isEmpty) throwISE("uninitialized value") }
def apply() = { ensureSet; value.get }
def :=(finalValue: T)(implicit credential: SetOnceCredential) {
value = Some(finalValue)
}
def allowAssignment = {
if (value.isDefined) throwISE("final value already set")
else new SetOnceCredential
}
private def throwISE(msg: String) = throw new IllegalStateException(msg)
@implicitNotFound(msg = "This value cannot be assigned without the proper credential token.")
class SetOnceCredential private[SetOnce]
}
object SetOnce {
implicit def unwrap[A](wrapped: SetOnce[A]): A = wrapped()
}
Run Code Online (Sandbox Code Playgroud)
我们获得了编译时安全性,:=不会意外调用,因为我们需要对象的SetOnceCredential,该对象仅返回一次。尽管如此,只要调用者拥有原始凭证,就可以重新分配变量。这适用于AnyVals 和AnyRefs。隐式转换允许我在许多情况下直接使用变量名,如果这不起作用,我可以通过附加().
典型用法如下:
object AppProperties {
private val mgr = new SetOnce[FileManager]
private val mgr2 = new SetOnce[FileManager]
val init /*(config: Config)*/ = {
var inited = false
(config: Config) => {
if (inited)
throw new IllegalStateException("AppProperties already initialized")
implicit val mgrCredential = mgr.allowAssignment
mgr := makeFileManager(config)
mgr2 := makeFileManager(config) // does not compile
inited = true
}
}
def calledAfterInit {
mgr2 := makeFileManager(config) // does not compile
implicit val mgrCredential = mgr.allowAssignment // throws exception
mgr := makeFileManager(config) // never reached
}
Run Code Online (Sandbox Code Playgroud)
如果在同一文件中的其他某个点,我尝试获取另一个凭据并重新分配变量(如 中所示calledAfterInit),但在运行时失败,则这不会产生编译时错误。