来自Java世界,最受欢迎的编程文本之一是Joshua Bloch的“Effective Java”。
该书中的许多主题之一指示程序员更喜欢基于枚举的单例到标准单例实现,因为大多数标准实现可以通过序列化,克隆等来打破。
我的问题
标准的Scala单例,似乎是通过使用“对象”而不是“类”来定义的,可以通过类似的技巧来打破吗?或者它是否受到运行时环境的保护,就像基于枚举的单例在Java中一样?
答案 0 :(得分:3)
如果你努力尝试,没有任何东西可以阻止你在Java中复制任何对象,包括枚举值。但是,由于您无法使用' normal'来创建enum
的实例。反思,你必须深入了解黑客工具箱:sun.misc.Unsafe
。这只用于创建实例,其余的可以用正常反射完成:
Unsafe unsafe = ...; // Obtain the value of the sun.misc.Unsafe.theUnsafe field, using normal reflection
try
{
Object o = unsafe.allocateInstance(TestEnum.class); // creates a new instance of TestEnum with all fields set to 0 / false / null
System.out.println(o); // prints 'null' because the name field is null
System.out.println(o.getClass()); // prints 'JavaTest$Enum'
Field f = Enum.class.getDeclaredField("name");
f.setAccessible(true); // bypasses the final and access checks
f.set(o, "TEST"); // set the name to 'TEST'
f = Enum.class.getDeclaredField("ordinal");
f.setAccessible(true);
f.set(o, 1); // set the ordinal to 1
System.out.println(o); // prints 'TEST'
System.out.println(((Enum) o).ordinal()); // prints 1
}
catch (Exception ex)
{
ex.printStackTrace();
}
此外,您可能希望将INSTANCE of TestEnum中的字段复制到新的TestEnum实例中。这可以像上面所示手动完成(通过将参数搞砸到f.set(o, ...)
一点),或者循环遍历所有字段并复制它们,如下所示:
for (Field f : TestEnum.class.getDeclaredFields())
{
if (!Modifiers.isStatic(f.getModifiers())
{
f.setAccessible(true);
f.set(o, f.get(TestEnum.INSTANCE));
}
}
当然这只复制TestEnum
类中的字段,在我的例子中没有字段。您可能还希望复制Enum
中的字段,因为它们不会被此for
循环处理。
要对您的问题给出正确的答案:是的,您可以打破Scala object
尽可能多地打破enum
。这一切都取决于你愿意花多少努力以及你的非法代码库有多大。