I'm asking a slight different question than this one. Suppose I have a code snippet:
def foo(i : Int) : List[String] = {
val s = i.toString + "!" //using val
s :: Nil
}
This is functionally equivalent to the following:
def foo(i : Int) : List[String] = {
def s = i.toString + "!" //using def
s :: Nil
}
Why would I choose one over the other? Obviously I would assume the second has a slight disadvantages in:
- creating more bytecode (the inner
def
is lifted to a method in the class) - a runtime performance overhead of invoking a method over accessing a value
- non-strict evaluation means I could easily access
s
twice (i.e. unnecesasarily redo a calculation)
The on开发者_运维问答ly advantage I can think of is:
- non-strict evaluation of
s
means it is only called if it is used (but then I could just use alazy val
)
What are peoples' thoughts here? Is there a significant dis-benefit to me making all inner val
s def
s?
1)
One answer I didn't see mentioned is that the stack frame for the method you're describing could actually be smaller. Each val
you declare will occupy a slot on the JVM stack, however, the whenever you use a def
obtained value it will get consumed in the first expression you use it in. Even if the def
references something from the environment, the compiler will pass .
The HotSpot should optimize both these things, or so some people claim. See:
http://www.ibm.com/developerworks/library/j-jtp12214/
Since the inner method gets compiled into a regular private method behind the scene and it is usually very small, the JIT compiler might choose to inline it and then optimize it. This could save time allocating smaller stack frames (?), or, by having fewer elements on the stack, make local variables access quicker.
But, take this with a (big) grain of salt - I haven't actually made extensive benchmarks to backup this claim.
2)
In addition, to expand on Kevin's valid reply, the stable val
provides also means that you can use it with path dependent types - something you can't do with a def
, since the compiler doesn't check its purity.
3)
For another reason you might want to use a def
, see a related question asked not so long ago:
Functional processing of Scala streams without OutOfMemory errors
Essentially, using def
s to produce Streams
ensures that there do not exist additional references to these objects, which is important for the GC. Since Stream
s are lazy anyway, the overhead of creating them is probably negligible even if you have multiple def
s.
The val is strict, it's given a value as soon as you define the thing.
Internally, the compiler will mark it as STABLE, equivalent to final in Java. This should allow the JVM to make all sorts of optimisations - I just don't know what they are :)
I can see an advantage in the fact that you are less bound to a location when using a def
than when using a val
.
This is not a technical advantage but allows for better structuring in some cases.
So, stupid example (please edit this answer, if you’ve got a better one), this is not possible with val
:
def foo(i : Int) : List[String] = {
def ret = s :: Nil
def s = i.toString + "!"
ret
}
There may be cases where this is important or just convenient.
(So, basically, you can achieve the same with lazy val
but, if only called at most once, it will probably be faster than a lazy val
.)
For a local declaration like this (with no arguments, evaluated precisely once and with no code evaluated between the point of declaration and the point of evaluation) there is no semantic difference. I wouldn't be surprised if the "val" version compiled to simpler and more efficient code than the "def" version, but you would have to examine the bytecode and possibly profile to be sure.
In your example I would use a val
. I think the val/def choice is more meaningful when declaring class members:
class A { def a0 = "a"; def a1 = "a" }
class B extends A {
var c = 0
override def a0 = { c += 1; "a" + c }
override val a1 = "b"
}
In the base class using def
allows the sub class to override with possibly a def that does not return a constant. Or it could override with a val. So that gives more flexibility than a val.
Edit: one more use case of using def over val is when an abstract class has a "val" for which the value should be provided by a subclass.
abstract class C { def f: SomeObject }
new C { val f = new SomeObject(...) }
精彩评论