13

I know this has been discussed on SO in other posts before and I understand the basic difference between the use of def and val. def is used for defining a method and val for an immutable reference. What I am trying to accomplish by asking this question is to understand if there is something more to def. Can it be used interchangeably with a val?

Recently I tried the following code and cannot convince myself if my present understanding of def is sufficient:

scala> def i: Int = 3
i: Int

scala> i
res2: Int = 3

So I am curious, is this equivalent to val i = 3?

Then I tried this:

scala> i()
<console>:9: error: Int does not take parameters
i()

I did this just to test my understanding of the semantics of def. Now I want to know, when i is a method, why Scala complains with "...does not take parameters"?

Next I tried the following:

scala> def i(): Int = 3
i: ()Int

scala> i()
res4: Int = 3

This time Scala seems to agree that i is a method. So can I use def in place of val interchangeable to declare and initialize a variable?

kiritsuku
  • 52,967
  • 18
  • 114
  • 136
ilango
  • 1,277
  • 3
  • 13
  • 19

3 Answers3

11

Both

def i = 3

and

def i() = 3

declare methods. The only difference is, that the first one is a method without a parameter list and the second is a method with an empty parameter list. The former is usually used for methods without side effects and the latter for methods with side effects. You should use a val instead of a def if the value never changes and you want to avoid recomputing it. A def gets recomputed every time it is called, while a val is assigned a value only once.

Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
  • 1
    Just to re-iterate, you can use either parens or no parens for any method that takes no parameters, using parens for methods with side effects is "only" convention, but I highly suggest you follow it. But the compiler won't stop you if you're doing something like `def dontDoThis = println("Hi!")` – adelbertc Oct 08 '12 at 19:08
  • Right answers. So extending this logic, I can say val list = List(1,2,3) and then I can call list.size() or list.length(). Both are methods without side effects, right? So I can also call them in this way: list.size or list.length – ilango Oct 08 '12 at 19:09
  • 3
    You can call a method that is declared with `()` without the parentheses, but you can't call a method that is declared without parentheses with parentheses. – Kim Stebel Oct 08 '12 at 19:18
  • I see. Subtle and makes sense. – ilango Oct 08 '12 at 19:30
  • Just for reference: you can define method with any number of empty parameter lists:`def i()()() = 3`. – incrop Oct 09 '12 at 07:38
  • @incrop, that is interesting. What is a use case for a method with any number of empty parameter lists? – ilango Oct 16 '12 at 16:17
  • Then you can only partialy apply function. Doesn't make sence with zero number of parameters. – user482745 Nov 05 '12 at 14:50
  • @ilango it also might have uses for machine-generated code, so you can avoid special cases. ("This generated method called _someMethod(...)(...)(...)_ always has three parameter lists. They just might be empty.") – James Moore May 25 '17 at 15:43
7

def defines a method, val defines an immutable value, as you already know.

One major difference is in when the expression on the right side of the = is evaluated. For a method, it is evaluated each time you call the method. For a value, it is evaluated when you initialize the value. See the difference:

scala> def i: Int = { println("Hello"); 3 }
i: Int

scala> i
Hello
res0: Int = 3

scala> i
Hello
res1: Int = 3

scala> val i: Int = { println("Hello"); 3 }
Hello
i: Int = 3

scala> i
res2: Int = 3
Jesper
  • 202,709
  • 46
  • 318
  • 350
  • Just read your comment after taking a break from SOF. Very useful input.Good answer. – ilango Oct 10 '12 at 20:00
  • See also my answer on the difference between `def`, `val` and `var` here: http://stackoverflow.com/questions/4437373/use-of-def-val-and-var-in-scala/4440614#4440614 – Jesper Mar 07 '14 at 08:18
6

Just to add on the top of the Kim's answer, you can override def by val.

// Entering paste mode (Ctrl+D to finish)

trait A {
  def i: Int
  def num: Long
}

class B extends A {
  val i = 7
  val num = 20L
}

// Exiting paste mode, now interpreting.

defined trait A
defined class B

scala> val b = new B
b: B = B@2d62bdd8

scala> b.i
res1: Int = 7

scala> b.num
res2: Long = 20
simhumileco
  • 31,877
  • 16
  • 137
  • 115
Eun Woo Song
  • 750
  • 6
  • 8