Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor.newInstance replaces Scala object

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?

like image 889
jqno Avatar asked Apr 16 '19 06:04

jqno


1 Answers

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).

like image 194
Krzysztof Atłasik Avatar answered Oct 23 '22 01:10

Krzysztof Atłasik