views:

78

answers:

5

Is there some way through the standard library or some already existing library to determine the degree of relation between two classes/interfaces in Java?

Let's say I have an object and a list of classes/interfaces. Now I basically want to know the one class of that list, that has the shortest inheritance-tree path to this object.

I already looked through the java.lang.reflect package and Class, but couldn't really find anything that would allow easy access to information like this. Is that perhaps already part of another library?

+1  A: 

this helps a bit. Not sure how to get the shortest path.

Tommy
+3  A: 

Reflection will let you get the parent class for any given class, so you can extract enough information to build yourself an inheritance tree, which you can then use to answer your question. I can't think of any built-in mechanism which will let you do that any more elegantly.

Carl Smotricz
+1  A: 

I don't know anything ready to use.

I would use Reflection to discover the relationships.

The hard part is the shortest-path.

  • You have to define what exactly you want :

    • For example, do you search superclasses first and then interfaces ?
    • What do you decide when several have the same length of path ?
      Use alphabetical order ?
      Use the order of discovery (which is random) ? ...
  • Then, look for these classes or interfaces in that order, starting with the current class, then it's parent class (and possibly implemented interfaces), and so on...

KLE
Thank you very much and for providing the list of things to keep in mind :-)
Horst Gutmann
@Host The best would have been a ready-to-use solution. But lacking one, I thought this could be useful ;-) Glad we share the same view :-)
KLE
A: 

This code should get you close. As others have stated, you may run into issues with interfaces though since the inheritance depth could easily be the same. You'll also need to add some null checking etc.

In this example FooBar3 extends FooBar2 extends FooBar.

public static void main(String[] args) {
    List<Class<?>> l = new ArrayList<Class<?>>() {{
        add(FooBar2.class);
        add(FooBar.class);
    } };
    System.out.println(getClosestParent(new FooBar3(), l));
 }

public static Class getClosestParent(Object o, List<Class<?>> classes) {
    List<Class<?>> related = getRelated(o, classes);
    Collections.sort(related, new Comparator<Class<?>>() {
        public int compare(Class<?> o1, Class<?> o2) {
            if (o1.isAssignableFrom(o2)) {
                return -1;
            } else if (o2.isAssignableFrom(o1)) {
                return 1;
            }
            return 0;
        }
    });
    return related.get(0);
}

public static List<Class<?>> getRelated(Object o, List<Class<?>> classes) {
    List<Class<?>> filtered = new ArrayList<Class<?>>();
    for (Class<?> aClass : classes) {
        if (aClass.isAssignableFrom(o.getClass())) {
            filtered.add(aClass);
        }

    }
    return filtered;
}
Shaun
+1  A: 

I couldn't help but find this a fun-sounding project. Here is prototype code that gives you the information you need. This code just attempts to calculate all of the possible inheritance paths from a given class to another. You could use this to get all of the paths from your source object to all of the possible classes you're interested in. As mentioned in other comments you may have to make a call about whether your prefer paths that use interfaces or not, but hopefully this code is helpful to you.

public class InheritenceDepth {

/**
 * Obtains a list of all the possible inheritance paths from the given targetClass
 * to the specified potentialAncestorClass.  If the targetClass does not extend or implement
 * the potentialAncestorClass the return list will be empty.
 */
public static List<InheritancePath> classInheritancePaths(Class<?> targetClass, Class<?> potentialAncestorClass){
    List<InheritancePath> returnList = new ArrayList<InheritancePath>();
    if(potentialAncestorClass.isAssignableFrom(targetClass)){

        if(potentialAncestorClass.equals(targetClass)){
            returnList.add(new InheritancePath(potentialAncestorClass));
        }

        if(targetClass.getSuperclass() != null){
            // try superclass
            List<InheritancePath> pathsFromSuperClass = 
                classInheritancePaths(targetClass.getSuperclass(), potentialAncestorClass);
            if(!pathsFromSuperClass.isEmpty()){
                for(InheritancePath path : pathsFromSuperClass){
                    path.add(targetClass);
                    returnList.add(path);
                }
            }
        }

        // try interfaces
        for(Class<?> interf : targetClass.getInterfaces()){
            List<InheritancePath> pathsFromInterface = 
                classInheritancePaths(interf, potentialAncestorClass);
            if(!pathsFromInterface.isEmpty()){
                for(InheritancePath path : pathsFromInterface){
                    path.add(targetClass);
                    returnList.add(path);
                }
            }
        }
    }
    return returnList;
}

/**
 * Represents the path from a base class to a superclass
 */
public static final class InheritancePath implements Iterable<Class<?>>{
    private List<Class<?>> path = new ArrayList<Class<?>>();
    public InheritancePath(Class<?> root){
        path.add(root);
    }

    void add(Class<?> pathElement){
        path.add(0, pathElement);
    }

    public Iterator<Class<?>> iterator(){
        return path.iterator();
    }

    public int depth(){
        return path.size();
    }

    public String toString(){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < path.size(); i++){
            sb.append(path.get(i).getName());
            if(i < path.size() - 1){
                sb.append(" -> ");
            }
        }
        return sb.toString();
    }
}

public static void main(String[] args) {
    List<InheritancePath> paths = classInheritancePaths(ConcurrentLinkedQueue.class, Collection.class);

    for(InheritancePath path : paths){
        System.out.println(path);
    }
}

}

BryanD
Thank you very much :-) I already had code for that problem for the most part in my mind but just wanted to check out, if there perhaps was some BSD-licensed library out there that I could use without moving my own stuff into a reusable library :-)
Horst Gutmann