0

I have a class X which is extended by various classes. X needs to know each subtype that exists of it at runtime. So my idea was to create a static method "static init()" in X that takes a Class as parameter. The problem is that a subclass of X is required to be initialized via its static-init-blocks before the call to init() happens. X for example may discover some of the static fields of the subclass which may be of any declared type. So take this code for example:

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);
    }
}

So what I am looking for is a way to somehow get my init()-method called as the last step in the static initialization of a class. Or any other way to prevent the AssertionError from happening.

Edit: Because this lead to misunderstanding in the comments, I actually want to do the second thing. I want my method to be called within the static initialization of any class that subclasses my class (either directly or indirectly). I don't know which subclasses exist, I don't know anything about their structure and all assertions must be checked at runtime (via reflection). My class and the subclasses may not even be compiled by the same compiler on the same machine and/or by the same people.

Edit2: Maybe I can build a ClassLoader that is a simple proxy to the System-ClassLoader and replace the System-ClassLoader with it. If the loaded class is then a subclass of my class I can initialize it and call my method. Unfortunately I don't know much about ClassLoaders (yet) and there are not much tutorials or help on the net regarding this topic. So can anyone having experience with custom ClassLoaders tell me if this is possible? What happens if some other library in my classpath does also install a custom ClassLoader at any point in time?

Edit3: What I don't want to do - if there is any other option - is direct byte-code modifications.

user1994405
  • 67
  • 2
  • 9
  • what are you trying to achieve here? why should a super class know about its subclasses? – giorashc Feb 27 '15 at 20:43
  • The first solution "init(getClass());" should work fine, if you do not call the constructor of X in the static initializer of Y (Type of a should not be a subtype of X). Must the solution capture the case "static X a = new X();" – CoronA Feb 27 '15 at 20:58
  • Am I right that your intension is that every subclass should fullfill a specific contract? In this case, that it should contain a field a not being null. – CoronA Feb 27 '15 at 21:08
  • Yes, Stefan Mandel, you are right. The rules that subclasses have to fulfill are however very complex. They involve structure of extending classes, various annotations and static and non-static fields. Yes, I constructed this case specifically to show a meaningful case where no instance of the subclass is ever created. This is why the first solution does not work. – user1994405 Feb 28 '15 at 11:13

3 Answers3

1

If you have to deal with a lot of reflection stuff i can recommend reflections libary

With which you can do:

    Reflections r = new Reflections("package.name");
    Set<Class<? extends X>> childs = r.getSubTypesOf(X.class);
Fish013
  • 11
  • 3
  • Thanks, but I don't like Google very much and I don't trust them. So I would prefer a pure-Java solution without some creepy lib. – user1994405 Feb 28 '15 at 11:20
  • And as far as I understand that, this only works within one package. So what is if my code-snippet would be part of a public library? There would be no chance of knowing in which packages the subclasses are. – user1994405 Feb 28 '15 at 11:39
  • Reflections is not google. And you may use it for the complete classpath (however it is a bit tricky to get that to work). A similar functionality is provided by guava Classpath (but this is google) and it is not restricted to packages. And you can of course do it your own way with [asm (only german)](https://jaxenter.de/classpath-scan-im-eigenbau-aus-der-java-trickkiste-12963). – CoronA Feb 28 '15 at 13:09
  • As you can read in the comments to a post below, basically the counter argument to that is performance if you have huge amount of classes. – user1994405 Feb 28 '15 at 13:34
  • was pretty sure this was google, but i was wrong, well its open source and not google, i use it a lot and its fine for me. the others already listed some pure java solutions – Fish013 Mar 01 '15 at 18:22
1

This would be an example for your 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);
    }
}
}

I think it does what you describe in Edit2, but it also suffers the weaknesses you mention (will be fragile with other class loading applications such as OSGI, Reflections, AOP, Spring).

To install this class loader you could load the Main class with this class loader and call the main Method on it with Reflection. You can find other more elegant solutions for setting a classloader on the web.

CoronA
  • 7,717
  • 2
  • 26
  • 53
0

X needs to know each subtype that exists of it at runtime.

This can be achieved with following code:

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);
    }
}

If you run main method, it will print something like:

[class Main$X, class Main$Y]
Aruziell
  • 243
  • 1
  • 8
  • As I already stated, I cannot guarantee that any instance of Y is ever created. Also I want my code be called when the static-init is finished. Which is both not the case at construction-time. – user1994405 Feb 28 '15 at 11:17
  • I slightly misunderstood your question. What about trying to look into whole class path and test every class for being extended from your base class? This is basically what @Fish013 suggested. You should also read [similar](http://stackoverflow.com/questions/492184/how-do-you-find-all-subclasses-of-a-given-class-in-java) questions. – Aruziell Feb 28 '15 at 12:14
  • Well... With about 100k classes in my classpath this can take a while. And there is another problem: WHEN shall I do that? Every Millisecond? For any intervall I pick I either end up in bad performance or I cannot guarantee that no object and no static field of type Y is used before my code is executed. The second thing is critical because if I cannot guarantee that, this may have a huge and uncontrolled impact. – user1994405 Feb 28 '15 at 13:15