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.
lazy val a3 = new A { def foo = 1 }
should have also been added to the question. – Skinflint