一个scala类中的val和对象?

时间:2010-08-10 12:04:53

标签: scala class object

在scala类中将字段声明为vallazy valobject之间有什么区别,如下面的代码段所示:

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 }
}

7 个答案:

答案 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的重要性,但不同的类型实际上代表了valsobjects之间的重要区别。特别是,vallazy val具有结构类型(例如A{def foo: Int}),而object具有单例类型。因此,对foo上的val方法的调用涉及反射,而fooobject方法的调用则不会:

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