33

What is the difference between declaring a field as val, lazy val and object inside a scala class, as in the following snippet:

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 }
}
Elazar Leibovich
  • 32,750
  • 33
  • 122
  • 169
PeWu
  • 631
  • 1
  • 7
  • 10
  • It turns out that `lazy val a3 = new A { def foo = 1 }` should have also been added to the question. – PeWu Aug 10 '10 at 12:45
  • See also [Scala - new vs object extends](http://stackoverflow.com/q/16182735/1048572) – Bergi May 25 '16 at 04:02

7 Answers7

22

In the former, any code included is executed as soon as class B is created. In the latter, however, until you actually use the object, it won't be instantiated.

You can see the difference here:

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

There are also hidden differences in what the created .class files are named and such; and of course the two have different types.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • 1
    So `object` works like `lazy val`. Are there any _practical_ differences between the two? – PeWu Aug 10 '10 at 12:41
  • 2
    Inherently, no, not as far as I know. The bytecode for `object` is a bit more compact. I'm not sure whether that means that `lazy val` is written inefficiently, or whether `object` might be unsafe under certain threading conditions, or both. – Rex Kerr Aug 10 '10 at 14:17
  • Or not as far as I remembered at that moment. See Alex Boisvert's comment for a critical difference! – Rex Kerr Aug 10 '10 at 14:56
  • Ouch! These two differences remind me the C++ gotchas (an object is just like a val, except that it is lazy, and except that when it is the field of a class and it is inherited, it cannot be overriden. Ouch. Maybe we should just use `lazy val`s instead of objects! – Elazar Leibovich Aug 10 '10 at 18:25
  • 3
    For what it's worth, lazy vals do seem to be preferred to object members in the Scala code I've read. If you see an object member in the wild, it's probably from pre-2.6 Scala, when lazy vals were introduced. – Dave Griffith Aug 11 '10 at 10:52
  • 1
    _Practical_ difference: invoking a method defined in an object is more efficient than invoking one defined in an anonymous class. – Aaron Novstrup Dec 08 '10 at 22:23
19

I'm not sure that aioobe recognized the significance of his answer, but the different types actually represent a critical difference between vals and objects. In particular, the val and lazy val have a structural type (e.g. A{def foo: Int}), while the object has a singleton type. As a result, calls to the foo method on the vals involve reflection, while calls to the foo method on the object do not:

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)
Community
  • 1
  • 1
Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
  • Zoiks! I didn't know anonymous classes used reflection. I guess using them for the pimp-my-library pattern is best avoided then: better to define a named class. Anonymous classes are used extensively in scala-swing but speed's not critical there. – Luigi Plinge Nov 02 '11 at 04:53
  • 3
    @Luigi Plinge Anonymous classes use reflection only for methods that are not defined in the parent class/interface. If `A` had defined `foo`, even abstractly, no reflection would have been needed. – Aaron Novstrup Nov 02 '11 at 21:26
18

One major difference is that val's can be overriden while objects can't.

class C extends B {                           
  override val a1 = new A { def foo = 2 }     
  override object a2 extends A { def foo = 2 }
}

leads to:

<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 }
Alex Boisvert
  • 2,850
  • 2
  • 19
  • 18
3

I suppose one difference is that a1 will be of one subtype of A while a2 will be of another subtype of A namely a2.type.

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> 
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • Not exactly. Both will be subclasses of `A`. – PeWu Aug 10 '10 at 12:09
  • Sure, but they will not be of the same type. – aioobe Aug 10 '10 at 12:10
  • 1
    To be more precise a2 will have a type a2.type. – venechka Aug 10 '10 at 12:29
  • 1
    Notice that your example Scala output contradicts what you say in the first sentence of your answer: the type of `a1` is *not* `A`; the type is the type of the anonymous subclass you created. You can make it an `A` by explicitly specifying the type: `val a1: A = new A {def foo=1}` – Jesper Aug 10 '10 at 18:42
  • @Jesper: in this specific example, yes. He did raise a valid point, however. Using an `object` *always* creates a new type, whereas using a `val` does not (unless you add behavior as in this case). – Aaron Novstrup Aug 10 '10 at 19:08
  • @Jesper: Just noticed this. The type of `a1` is _not_ the type of the anonymous subclass; it's the structural type `A{def foo: Int}`, which differs from the anonymous subclass in that invoking `foo` will use reflection. You can verify this by having `foo` print a stack trace. – Aaron Novstrup Dec 08 '10 at 21:43
  • @Aaron: Yes, that's right, and because it's going to be called using reflection, the call will be much slower than a regular method call. – Jesper Dec 09 '10 at 14:54
3

Another major difference is that objects know their own name and val's do not.

Eric Pabst
  • 536
  • 4
  • 12
2

The first practical difference is that lazy vals and objects are lazy, whereas vals are eager.

The main difference between objects and lazy vals is that an object is, from the languages perspective considered to be a "singleton," which from the jvm's perspective is generally treated as a static member. The object definition in the given example cannot be overriden, as others have demonstrated, for the same reason static members cannot be overriden: without being tied to an instance, there's no conceivable way to do a virtual function lookup.

object Foo { object Bar extends A; }

is loosely like the following java code:

class Foo { 
  private static class Bar extends A{}
  public static Bar Bar = new Bar;
}

If in the above example, if a subclass C extends Foo was defined, it would not be able to override the definition of Bar. The static instance Bar in Java would be accessed as Foo.Bar. C.Bar does not mean the same thing as (new C).Bar. I might be a bit off here, I haven't actually tried de-compiling scala code, this is just an example to illustrate the general concept of objects as static members.

lazy vals can be a bit less efficient. Last time I checked, they were implemented by maintaining a hidden field in the class that kept track of which lazy vals had been initialized. Maintaining this field requires locking, which can cause performance issues.

One major practical difference between lazy val and object is treatment of failure:

If I have:

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$

whereas if I do:

object Stuff2 { lazy val Bar = new Foo() { val x = "hi" } }
Stuff2.Bar
// "blah!"
Stuff2.Bar.x
// "blah!"

The "NoClassDefFoundError" can be really confusing, and since it's an Error not an Exception it can break error handling code that (appropriately) catches/logs "Exception" but allows errors to propagate. I might even consider this sort of a bug in the Scala language, since this use-case does in fact indicate an exceptional condition, not truly a JVM error. I've seen NoClassDefFoundErrors when accessing objects that depended on external resources (e.g. database connections or files on disk). Only the first access logs the underlying cause, so proper debugging of such an issue typically requires restarting your application server.

nairbv
  • 4,045
  • 1
  • 24
  • 26
0

This is not a structural type: val a = new A { def foo = 1 }

It creates a unique anonymous subclass; a.foo invokes foo in that class.

x here is a structural type: def bar( x: { def bass: Int} )

x.bass will introspect x (of unknown type) to find a method with name 'bass'. It will work with fish or musical instruments. ;)

One difference between the lazy val and object is this:

var someA = (new B).a3
someA = (new B).a3 // ok

var anotherA = (new B).a2
anotherA =  = (new B).a2 // compile error
Mark
  • 1