Scala反射:实例化单例对象 [英] Scala Reflection: instantiating a singleton object

查看:162
本文介绍了Scala反射:实例化单例对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码实例化一个scala对象.这可行,但是似乎存在一个问题:println两次打印出 ,每次都使用另一个哈希码.

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

结果:

init472467991
init2051378291
e: Any = Test2$@7a458c73

ehashCode等于后一个(2051378291).我想知道为什么会这样,因为据我所知应该只有一个?

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?

使用Scala 2.12.4版本

using scala version 2.12.4

推荐答案

JVM没有单例*

您正在调用类的私有构造函数. Scala反射允许它.并且,当您调用构造函数时,您将获得一个新的实例.

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.

用纯Java制作单例实际上非常困难,因为除了使用new Something以外,还有许多构造实例的方法.例如,反序列化除了对象.并且sun.misc.Unsafe#allocateInstance可以在没有 sans java.lang.Class 的情况下调用任何类的新实例调用任何构造函数代码.

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在后台进行了一些工作,以确保您在任何正常使用期间都不会意外创建第二个实例(例如,它隐藏了构造函数并处理了反序列化),但是它不能保护您免受故意创建一个.当您开始使用反射时,您正是这样做的.

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.

甚至Java enum都可以在运行时使用反射实例化.您不能直接在枚举上调用Class#newInstance(该实现禁止这样做),但是了解一些内部细节可以使您到达那里**

Even Java enums 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)

(交互式版本@ Scastie)

要获取默认"实例,请在以下位置运行整个scala编译器运行时

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.

顺便说一句,可能会出现ScalaReflectionException尝试在工作表模式下在IntelliJ工作表或Scastie中运行您的代码的情况,因为这些东西会将您的代码包装在使用主方法的另一个对象中

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

*仅在几种版本的HotSpot JVM上进行了测试

* Only tested on few versions of HotSpot JVM

**请不要在任何认真的代码中执行此操作!我仅以此来证明观点.这也很没用,因为它不会更改valuesvalueOf.是的,我只在JDK8随附的HotSpot上进行了检查.

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

这篇关于Scala反射:实例化单例对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆