我想强制使另一个库中的类向子类开放。类本身是公共的,但它唯一的构造函数是包私有的。我已经探索过使用字节码生成一个类来打开构造函数并进行
super()
调用。在JASM(超级酷)中,类看起来像这样:
public class com/internal/OpenChild
extends com/external/Parent
{
public <init>()V {
try {
ldc com/external/Parent
iconst 1
anewarray java/lang/Class
invokevirtual java/lang/Class.getDeclaredConstructor([java/lang/Class)java/lang/reflect/Constructor
iconst 1
invokevirtual java/lang/reflect/Constructor.setAccessible(Z)V
} catch (java/lang/Throwable) {
astore 0
new java/lang/RuntimeException
dup
aload 0
invokespecial java/lang/RuntimeException.<init>(java/lang/Throwable)V
athrow
}
aload 0
invokespecial com/external/Parent.<init>()V
return
}
}
这个类,
com.internal.OpenChild
,扩展了com.external.Parent
。它的公共无参数构造函数可以访问后者的包私有无参数构造函数,并随后进行 super
调用。等效的 Java 可能看起来像这样:
package com.internal;
import com.external.Parent;
public class OpenChild extends Parent {
public OpenChild() {
try {
Parent.class.getDeclaredConstructor().setAccessible(true);
} catch (Throwable t) {
throw new RuntimeException(t);
}
super();
}
}
虽然反射工作正常,但 super 调用失败并出现 IllegalAccessException。我相信我可能还抱有希望——毕竟,可以使用反射来创建
com.external.Parent
的实例。我的策略中缺少什么?难道这样的黑魔法根本就无法发动吗?
对
setAccessible()
的调用设置被调用对象的状态。这意味着您的代码必须如下所示:
package com.internal;
import com.external.Parent;
public class OpenChild extends Parent
{
public OpenChild()
{
try
{
final var constructor = Parent.class.getDeclaredConstructor();
constructor.setAccessible( true );
return constructor.newInstance();
}
catch( Throwable t )
{
throw new RuntimeException( t );
}
}
}
不要将其输入到您的 IDE 中,它不会工作……首先,您不能在构造函数中使用带有值的
return
。
此外,
newInstance()
调用还会调用“超级构造函数”,这不会在当前实例的对象初始化上下文中发生。
这意味着只有
private
构造函数是防止类被子类化的一个非常好的方法。