Scala Reflection:实例化单例对象

时间:2018-02-02 08:51:45

标签: scala object reflection instantiation

我使用以下代码实例化scala对象。这有效,但似乎有一个问题:println打印出两次,每次都有另一个哈希码。

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

结果:

init472467991
init2051378291
e: Any = Test2$@7a458c73

hashCode的{​​{1}}等于后者(2051378291)。我想知道为什么这是因为据我所知,应该只有一个?

编辑:使用scala版本2.12.4

1 个答案:

答案 0 :(得分:3)

JVM没有单例*

您正在调用类的私有构造函数。 Scala反射允许它。当你调用一个构造函数时,你会得到一个新实例。

在普通Java中制作单例实际上非常困难,因为除了使用new Something之外,还有一些方法可以构建实例。例如,反序列化might not call any constructors besides one of Object。并且sun.misc.Unsafe#allocateInstance可以在不调用任何构造函数代码的情况下召唤任何类sans java.lang.Class的新实例。

Scala object做了一些工作,以确保您在正常使用期间不会意外创建第二个实例(例如,它隐藏构造函数并处理反序列化),但它无法保护您来自故意创建一个。当你开始使用反射时,你就是这么做的。

甚至Java enum也可以在运行时使用反射进行实例化。你不能直接在枚举上调用Class#newInstance(实现禁止它),但了解一些内部细节可以帮助你**

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)

获得"默认"例如,您can access使用反射命名为MODULE$的公共静态字段。您还可以run whole scala compiler at runtime

对于你来说,不管你想要实现什么,不要依赖反思,这可能是你最好的选择。

顺便说一句,有可能让ScalaReflectionException尝试在工作表模式下运行IntelliJ工作表或Scastie中的代码,因为这些东西用main方法将代码包装在另一个对象中

*仅在少数版本的HotSpot JVM上测试

**请不要在任何严肃的代码中执行此操作!我只是用它来证明一点。这也没用,因为它不会更改valuesvalueOf。是的,我只在JDK8附带的HotSpot上查看过它。