I was debugging a deserialization issue with Jackson where Scala object
instances seemed to be replaced. I managed to drill the issue down to this code:
object WaitWhat extends App {
object XX
val x1 = XX
// Notice: no assignment!
XX.getClass.getConstructor().newInstance()
val x2 = XX
println(x1)
println(x2)
}
The output is:
WaitWhat$XX$@5315b42e
WaitWhat$XX$@2ef9b8bc
(Of course the actual hash codes change each run.)
IntelliJ's debugger also indicates that x1
and x2
really are different instances, despite the fact that the result of newInstance
is completely ignored.
I would have expected a no-op, or an exception of some kind. How is it possible that the actual object instance gets replaced by this call?
Objects in Scala have a private constructor that can’t be called with new (since it’s private), but can still be called using reflection.
Under the hood, the object is accessed by static MODULE$
field. This field is the singleton instance created internally by calling the private constructor.
As long as you access the object in your Scala or in your Java code using MODULE$
you will be ok. However, you can't be sure, that some library won't create an additional instance of your object with a private constructor using reflection. In this case, whenever private constructor will be called, a new instance of the object will be created and reassigned to MODULE$
.
This can happen especially if you use Java libraries, that are not aware of the existence of Scala objects.
Please check this article for more details.
Anyway, I would just create custom deserializer for Jackson (similarly to the solution described in the article).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With