I'm using the following code to instantiate a scala object. This works, but there seems to be one problem: the println
is printed out twice, each time with another hashcode.
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{universe => ru}
object Test2 { println("init"+hashCode())}
val mirror = ru.runtimeMirror(getClass.getClassLoader)
val m = ru.typeOf[Test2.type].members.filter(_.isConstructor).head.asMethod
val m2 = mirror.reflectClass(typeOf[Test2.type].typeSymbol.asClass)
val cm = m2.reflectConstructor(m)
val e = cm.apply()
Results in:
init472467991
init2051378291
e: Any = Test2$@7a458c73
the hashCode
of e
is equal to the latter one (2051378291). I'm wondering why this is because as far as I know there should be only one?
EDIT: using scala version 2.12.4
JVM has no singletons*
You're invoking a private constructor of a class. Scala reflection allows it. And when you invoke a constructor, you get a new instance back.
It's actually pretty hard to make a singleton in plain Java because there are ways to construct an instance except using new Something
. For instance, de-serialization might not call any constructors besides one of Object. And there's sun.misc.Unsafe#allocateInstance
that can conjure new instances of any class sans java.lang.Class
without calling any constructor code.
Scala object
does some job behind the hood to ensure you don't accidentally create a second instance during any normal usage (e.g. it hides the constructor and handles de-serialization), but it cannot protect you from deliberately creating one. When you start using reflection, you do exactly that.
Even Java enum
s can be instantiated at runtime using reflection. You cannot call Class#newInstance
on an enum directly (the implementation forbids it), but knowing a bit of internal details can get you there**
import java.nio.file.StandardOpenOption // first std enum I could remember for a quick dirty sample
val ctor = classOf[StandardOpenOption].getDeclaredConstructors.head
val aac = ctor.getClass.getDeclaredMethod("acquireConstructorAccessor")
aac.setAccessible(true) // unlimited power!
val ctorAccess = aac.invoke(ctor)
val newInstanceCall = ctorAccess.getClass.getDeclaredMethod("newInstance", classOf[Array[AnyRef]])
newInstanceCall.setAccessible(true)
// note that it does not throw ClassCastException, so it's a fine instance
val uhOh = newInstanceCall.invoke(ctorAccess, Array("UhOh", 42)).asInstanceOf[StandardOpenOption]
assert(uhOh.name == "UhOh")
assert(uhOh.ordinal == 42)
(interactive version @ Scastie)
To get the "default" instance, you can access a public static field named MODULE$
using reflection. You can also run whole scala compiler at runtime
It's likely to be the best bet for you to not rely on reflection in whatever you're trying to achieve.
BTW, it is possible to get ScalaReflectionException
trying to run your code in IntelliJ worksheet or Scastie in worksheet mode, because these things wrap your code in another object with main method
* Only tested on few versions of HotSpot JVM
** Please don't do this in any serious code! I only use this to prove a point. This is also pretty useless because it does not change values
or valueOf
. And yes, I only checked it on HotSpot that comes with JDK8.