简体   繁体   中英

val and object inside a scala class?

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

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.

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 (eg A{def foo: Int} ), while the object has a singleton type. As a result, calls to the foo method on the val s 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)

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 }

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> 

另一个主要区别是对象知道他们自己的名字而val不知道。

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 (eg 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM