我有一个类X,它由各种类扩展。 X需要在运行时知道它存在的每个子类型。所以我的想法是在X中创建一个静态方法“static init()”,它将Class作为参数。问题是在调用init()之前,需要通过其static-init-blocks初始化X的子类。例如,X可能会发现子类的一些静态字段,这些字段可能是任何声明的类型。所以请以此代码为例:
class X {
X() {
/*This would be one idea but does not work
in this concrete example.*/
init(getClass());
}
static void init(Class<? extends X> c) {
if(c.getDeclaredField("a").get(null) == null) {
throw new AssertionError();
}
}
}
class Y extends X {
static X a = new X();
static {
/*Another idea. Works totally fine but
I dont want to trust subclasses that
they do that correctly.*/
init(Y.class);
}
}
所以我正在寻找的方法是以某种方式获取我的init()方法,该方法被称为类的静态初始化的最后一步。或者任何其他方法来防止AssertionError发生。
编辑:因为这导致评论中的误解,我实际上想要做第二件事。我希望在我的类的子类(直接或间接)的任何类的静态初始化中调用我的方法。我不知道哪些子类存在,我对它们的结构一无所知,所有断言必须在运行时检查(通过反射)。我的类和子类甚至可能不会由同一台机器上和/或同一个人编译。
Edit2:也许我可以构建一个ClassLoader,它是System-ClassLoader的一个简单代理,并用它替换System-ClassLoader。如果加载的类是我的类的子类,我可以初始化它并调用我的方法。不幸的是,我对ClassLoaders(还)知之甚少,关于这个主题的网上没有太多的教程或帮助。任何有定制ClassLoaders经验的人都可以告诉我这是否可行?如果我的类路径中的其他库也在任何时间点安装了自定义ClassLoader,会发生什么?
Edit3:我不想做的事情 - 如果还有其他选择 - 是直接字节码修改。
答案 0 :(得分:1)
如果你需要处理很多反思,我可以推荐reflections libary
您可以做到:
Reflections r = new Reflections("package.name");
Set<Class<? extends X>> childs = r.getSubTypesOf(X.class);
答案 1 :(得分:1)
这将是Edit2的一个示例:
public class ConstraintClassLoader extends URLClassLoader {
public ConstraintClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
c = super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
System.out.println("find " + name);
Class<?> c = super.findClass(name);
Class<?> parent = c.getSuperclass();
while (parent != null) {
if (parent.getName().contains("X")) {
break;
}
parent = parent.getSuperclass();
}
if (parent == null) {
return c;
}
Field declaredField = c.getDeclaredField("a");
declaredField.setAccessible(true);
if (declaredField.get(null) == null) {
throw new AssertionError();
}
return c;
} catch (NullPointerException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException | AssertionError e) {
throw new RuntimeException(e);
}
}
}
我认为它完成了你在Edit2中所描述的内容,但它也会遇到你提到的弱点(对于其他类加载应用程序(如OSGI,Reflections,AOP,Spring)会很脆弱。)
要安装此类加载器,您可以使用此类加载器加载Main类,并使用Reflection调用其上的main方法。您可以在网上找到其他更优雅的解决方案来设置类加载器。
答案 2 :(得分:0)
X需要知道运行时存在的每个子类型。
这可以通过以下代码实现:
import java.util.HashSet;
import java.util.Set;
public class Main {
public static class X {
private static Set<Class<? extends X>> classes = new HashSet<Class<? extends X>>();
public X() {
classes.add(getClass());
// and here you can check for fields of subclasses
}
}
public static class Y extends X {}
public static void main(String[] args) {
new Y(); new X();
System.out.println(X.classes);
}
}
如果您运行main方法,它将打印如下内容:
[class Main$X, class Main$Y]