在scala类中将字段声明为val
,lazy val
和object
之间有什么区别,如下面的代码段所示:
class A
class B {
val a1 = new A { def foo = 1 }
object a2 extends A { def foo = 1 }
lazy val a3 = new A { def foo = 1 }
}
答案 0 :(得分:20)
在前者中,包含的任何代码都会在创建B类后立即执行。但是,在后者中,直到您实际使用该对象,它才会被实例化。
你可以在这里看到不同之处:
class A { println("Creating a new A") }
class B {
val a1 = new A { println("a1"); def foo = 1 }
object a2 extends A { println("a2"); def foo = 1 }
}
scala> val b = new B
Creating a new A
a1
b: B = B@1176e8a
scala> b.a2.foo
Creating a new A
a2
res0: Int = 1
创建的.class文件的命名方式也存在隐藏的差异;当然,两者有不同的类型。
答案 1 :(得分:19)
我不确定aioobe是否认识到his answer的重要性,但不同的类型实际上代表了vals
和objects
之间的重要区别。特别是,val
和lazy val
具有结构类型(例如A{def foo: Int}
),而object
具有单例类型。因此,对foo
上的val
方法的调用涉及反射,而foo
上object
方法的调用则不会:
class A
class B {
val a1 = new A { def foo = printStack }
object a2 extends A { def foo = printStack }
lazy val a3 = new A { def foo = printStack }
def printStack() =
new Exception().getStackTrace take 3 foreach println
}
scala> val b = new B
b: B = B@5c750
scala> b.a1.foo // the val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$1.foo(<console>:7)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
scala> b.a2.foo // the object
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$a2$.foo(<console>:8)
line128$object$$iw$$iw$.<init>(<console>:9)
scala> b.a3.foo // the lazy val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$2.foo(<console>:9)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
答案 2 :(得分:18)
一个主要区别是val可以被覆盖,而对象则不能。
class C extends B {
override val a1 = new A { def foo = 2 }
override object a2 extends A { def foo = 2 }
}
导致:
<console>:9: error: overriding object a2 in class B of type object C.this.a2;
object a2 cannot be used here - classes and objects cannot be overridden
override object a2 extends A { def foo = 2 }
答案 3 :(得分:3)
我认为一个区别是,a1
属于A
的一个子类型,而a2
属于A
的另一个子类型,即a2.type
。< / p>
scala> class A
defined class A
scala> val a1 = new A {def foo = 1}
a1: A{def foo: Int} = $anon$1@a9db0e2
scala> object a2 extends A {def foo = 1}
defined module a2
scala> a1
res0: A{def foo: Int} = $anon$1@a9db0e2
scala> a2
res1: a2.type = a2$@5b25d568
scala>
答案 4 :(得分:3)
另一个主要区别是对象知道他们自己的名字而val不知道。
答案 5 :(得分:2)
第一个实际区别是懒惰的vals和对象是懒惰的,而val是渴望的。
对象和惰性值之间的主要区别在于,从语言角度来看,对象是被认为是&#34;单身,&#34;从jvm的角度来看,它通常被视为静态成员。正如其他人所证明的那样,给定示例中的对象定义不能被覆盖,出于同样的原因,静态成员不能被覆盖:没有绑定到实例,没有可能的方法来进行虚函数查找。
object Foo { object Bar extends A; }
与以下java代码非常相似:
class Foo {
private static class Bar extends A{}
public static Bar Bar = new Bar;
}
如果在上面的例子中,如果定义了子类C extends Foo,它将无法覆盖Bar的定义。 Java中的静态实例Bar将作为Foo.Bar进行访问。 C.Bar与(新C)并不是一回事.Bar。我可能有点偏离这里,我实际上没有尝试去编译scala代码,这只是一个例子来说明对象作为静态成员的一般概念。
懒惰的val可能效率低一点。上次我检查时,它们是通过在类中维护一个隐藏的字段来实现的,该字段跟踪已初始化了哪些惰性值。维护此字段需要锁定,这可能会导致性能问题。
懒惰与对象之间的一个主要实际区别是失败的处理:
如果我有:
class Foo() { throw new Exception("blah!"); }
object Stuff { object Bar extends Foo { val x = "hi" } }
Stuff.Bar
//exception "blah!" thrown.
Stuff.Bar.x
//NoClassDefFoundError: Could not initialize Stuff$Bar$
而如果我这样做:
object Stuff2 { lazy val Bar = new Foo() { val x = "hi" } }
Stuff2.Bar
// "blah!"
Stuff2.Bar.x
// "blah!"
&#34; NoClassDefFoundError&#34;可能真的令人困惑,因为它是一个错误而不是异常,它可以打破错误处理代码(适当地)捕获/记录&#34;异常&#34;但允许错误传播。我甚至可能会考虑Scala语言中的这种错误,因为这个用例实际上表明了一个异常情况,而不是真正的JVM错误。我在访问依赖外部资源的对象(例如数据库连接或磁盘上的文件)时看到过NoClassDefFoundErrors。只有第一次访问才会记录根本原因,因此正确调试此类问题通常需要重新启动应用程序服务器。
答案 6 :(得分:0)
这是不结构类型:val a = new A {def foo = 1}
它创建了一个独特的匿名子类; a.foo在那个类中调用foo。
x这里是结构类型:def bar(x:{def bass:Int})
x.bass将内省x(未知类型)以找到名为“bass”的方法。它适用于鱼类或乐器。 ;)
lazy val和object之间的一个区别是:
var someA = (new B).a3
someA = (new B).a3 // ok
var anotherA = (new B).a2
anotherA = = (new B).a2 // compile error