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