Home > Net >  why does my main object, invoke all methods of another object, when only one is actually invoked?
why does my main object, invoke all methods of another object, when only one is actually invoked?

Time:03-20

Day 1 of learning scala :
This is my code, but I get unexpected results in one scenario.

object Hello extends App {
  def square(x: Int) = x * x
  println("Hello, World!")
}

object main extends App {
  println(Hello.square(2))
}

then the output is (correct )

4

but when I run main without Hello extending App as well,

object Hello {
  def square(x: Int) = x * x
  println("Hello, World!")
}

object main extends App {
  println(Hello.square(2))
}

I get this.

Hello, World!
4

What is happening? Why is main executing everything inside the Hello object, when I simply evoked the Hello.square function?

CodePudding user response:

My understanding is that everything in the object block is evaluated when you reference the object. And

object Hello {
  def square(x: Int) = x * x
  println("Hello, World!")
}

is equivalent to

object Hello {
  def square(x: Int) = x * x
  val _ = println("Hello, World!")
}

so the printing is just a side effect that is produced when evaluating the object. Also take note of the following example:

object Hello {
  def square(x: Int) = x * x
  println("Hello, World!")
}

object main extends App {
  println("first")
  Hello
  println("second")
  println(Hello.square(2))
  println("third")
}

this prints

first
Hello, World!
second
4
third

so the printing of hello world happens only once and it happens when Hello is referenced, not when square() is called.

Normally, you would not want to have code that produces side effects within an object. This is more a place for functions and values/variables. E.g.:

object Hello {
  val w = "World"
  def greet(what: String): Unit = println(s"Hello, $what!")
}

object main extends App {
  Hello.greet(Hello.w)
  Hello.greet("Scala")
}

Or if you want the value to be dynamic, you can use a class instead:

class Hello(val w: String) {
  def greet(what: String): Unit = println(s"Hello, $what!")
}

object main extends App {
  val h = Hello("World")
  h.greet(h.w)
  h.greet("Scala")
}

CodePudding user response:

Using an Object is essentially using a static class so all the code inside it evaluated when the object is constructed. So, actually the expected result is for the println to happen regardless if you call something on the object or not.

The question then, is how come you don't see this behavior with

object Hello extends App {
  def square(x: Int) = x * x
  println("Hello, World!")
}

well, the only difference is the extends App and indeed in the documentation we can see

Caveats

It should be noted that this trait is implemented using the DelayedInit functionality, which means that fields of the object will not have been initialized before the main method has been executed.

So when your first implementation builds on a limitation of the App implementation - which will probably change as

Future versions of this trait will no longer extend DelayedInit.

It is better to put any "free" code in Hello inside functions to prevent the unintended side effect you see

  • Related