2013-01-18 46 views
2

我知道有多个问题可以解决相关问题,但我不确定它确实发生了我正在寻找的内容。经过几年的Java开发,我仍然是Scala的新手。我正在寻找最好的方法来测试一个对象是否已经初始化,如果没有,则初始化它。例如,在Java:Scala - 处理对象的初始化(_对选项[T])

private MyObject myObj = null; 

,并在未来的某个时刻:

public void initMyObj(){ 
    if (myObj == null){ 
     myObj = new MyObj(); 
    } 
    // do something with myObj 
} 

在此之后,我可能会重新分配MyObj中一个不同的对象,但它是不可能的。在Scala中,我有这样的:

class Test { 
    var myObj: MyObj = _ 
} 

我读过,我可以使用选项,而不是,是这样的:

var myObj = None : Option[MyObj] 

,然后我检查:

myObj match { 
    case None => ... 
    case Some(value) => ... 
} 

但感觉当我可能不会在其他任何时候在其他地方进行这种检查时,我会采用这种模式 - 虽然对于Scala来说如此新颖,但我可能是错的。这是实现我想要的最好方式还是有没有其他选项不涉及选项?

回答

4

在Scala中通常不会将部分构建的对象放在周围。你通常会重新思考你的对象是如何被实例化的,以查看你是否不能使用不太脆弱的不同模式。例如,而不是设置初始化变量的方法:

class Foo { var a: String = null; var b: String = null } 
def initFooA(s: String, f: Foo) { if (f.a == null) f.a = s } 
def initFooB(s: String, f: Foo) { if (f.b == null) f.b = s } 
f 
initFooA("salmon", f) 
// Do stuff 
initFooB("herring", f) 

你会尝试重组你的代码来生成你需要对需求的值,延缓foo的实例化,直到然后:

case class Bar(a: String, b: String) {} 
def initBarA(s: String) = s 
def initBarB(s: String) = s 
val iba = initBarA("halibut") 
// Do stuff 
val ibb = initBarB("cod") 
Bar(iba, ibb) 

因为Scala可以方便地访问元组(和类型推断),所以这比Java中的痛苦小得多。

你可以做的另一件事是推迟后期初始化给其他人。

case class Baz(a: String)(bMaker: => String) { 
    lazy val b = bMaker 
} 

现在你传入一些参数b,并安排它处理任何需要处理的后期初始化事件。这并不总是避免需要设置变量,但它可以帮助将它从您的类代码中移出到您的初始化逻辑中(通常这对于它来说是更好的选择)。

用vars做这件事不那么简单。实际上,你可能是最好的选择,例如,通过:

class LazyVar[A](initial: => A) { 
    private[this] var loaded = false 
    private[this] var variable: A = _ 
    def apply() = { if (!loaded) { loaded = true; variable = initial }; variable } 
    def update(a: A) { loaded = true; variable = a } 
} 

然后你(可悲)不得不使用()每读写。

scala> val lv = new LazyVar({ println("Hi!"); 5 }) 
lv: LazyVar[Int] = [email protected] 

scala> lv() 
Hi! 
res2: Int = 5 

scala> lv() = 7 

scala> lv() 
res4: Int = 7 

然后你使用这个类,而不是实际var的实例,通过懒惰初始化。 (lazy val非常类似于引擎盖;编译器只是保护您不会注意到。)

最后,如果您想要一个功能齐全的对象偶尔缺少一个值,var x: Option[X]是您想要的构造使用;如果你无法找到解决标准Java创建模式的方法(并且你不想尝试一些更具异国情调的东西,比如使用越来越多的信息创建对方的对象,或者是因为性能很重要,而且你负担不起,或者你不喜欢编写那么多的样板文件以允许类型检查来验证你的对象是否被正确创建),但是否则你想要使用它,var x: X = null是我选择的,而不是_。如果X是原语,则可能需要明智地选择正确的值(例如,Double.NaN而不是0.0,-1而不是0对于Int)以指示I-am-not-initialized。如果它是通用代码,并且您希望Any而不是AnyRef,asInstanceOf-来回之间AnyAnyRef之间可能是最好的方式出来typechecked很差的情况下(假设你真的,真的不能使用Option,在这一点上是多更清晰)。

+0

感谢雷克斯。我喜欢延迟初始化。在我的情况下,我需要初始化一个变量。是否有可能以其他方式使用延迟初始化? – Dan

+0

@丹 - 排序;我已经修改了我的答案。 –

5

也许一个懒惰的变量是你需要的。

lazy val myObj: MyObj = //here you put the object creation code 

通过这种方式,对象创建被推迟到代码第一次尝试访问它。

+0

是不是只允许使用值定义?没有变数? – Dan

+0

@Dan:正确。只有'lazy val'不是'lazy var'。 –

+0

我的错,对不起,错字 –