使用包私有构造函数强制子类化 Java 类

问题描述 投票:0回答:1

我想强制使另一个库中的类向子类开放。类本身是公共的,但它唯一的构造函数是包私有的。我已经探索过使用字节码生成一个类来打开构造函数并进行

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
的实例。我的策略中缺少什么?难道这样的黑魔法根本就无法发动吗?

java oop reflection jvm
1个回答
0
投票

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
构造函数是防止类被子类化的一个非常好的方法。

© www.soinside.com 2019 - 2024. All rights reserved.