-3

Please create an algorithm that takes a list of classes and sorts them in a way, that whenever

  1. Class A is subclass of class B

  2. Class A implements interface B

The index of B is smaller than A.

What I tried so far is,

public void sortClasses(Class... classes) {
        List<Class> classesToSort = new ArrayList<>();
        for(Class c : classes) {
            Class superClass = c.getSuperclass();
            if(superClass != null) {
                classesToSort.add(superClass);
            }
            Class[] interfaces = c.getInterfaces();
            if(interfaces.length > 0) {
                classesToSort.addAll(Arrays.asList(interfaces));
            }
            classesToSort.add(c);
        }
    }

I'm not sure whether this works or not.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
kinjal patel
  • 396
  • 3
  • 12
  • 4
    We aren't going to do your homework for you. What have you tried so far, and what exactly was the problem? – Mureinik Jun 19 '18 at 06:25
  • 2
    Funny question. – Ilan Keshet Jun 19 '18 at 06:27
  • This is the problem in fact. Given a list of classes and sort them according to above condition. We need to write a function which takes a list of classes and sort it accordingly – kinjal patel Jun 19 '18 at 06:28
  • @Mureinik: I edited my question, hope this helps or give me suggestion what can be done or is the proper way to do it – kinjal patel Jun 19 '18 at 06:32
  • Your code doesn't really sort anything, it just creates a local list and then returns. Maybe you want to return the `classesToSort`? The next question is whether that list is sorted right; try it out on some classes, and if you come across a specific problem, post a question regarding that. – daniu Jun 19 '18 at 06:36
  • @daniu: Yes, please assume it returns classesToSort. – kinjal patel Jun 19 '18 at 06:38
  • An interface might be implemented several times in the inheritance hierarchy. And interfaces may extend other interfaces. `A extends B implements L; B implements K; K extends L.` Enjoy. – Joop Eggen Jun 19 '18 at 06:46
  • You're looking for a [topological ordering](https://en.wikipedia.org/wiki/Topological_sorting) of classes. That problem can be solved with a depth-first-search and some other approaches. – yeputons Jun 19 '18 at 12:25

3 Answers3

2

If you hear "sorting" in Java, always think "Comparator". If you have a Comparator which is able to compare two elements of a given type (in your case, Class), you can sort a list of those elements using Collections.sort(elements, comparator).

To write a Comparator, you need to implement its method

public int compare(E el1, E el2);

with E being the type of the elements, so in your case

public int compare(Class<?> c1, Class<?> c2);

because you're comparing Class objects. You need to return -1 if c1 < c2, or 1 if c2 < c2, or 0 if they're equal (for this comparison).

Now you have two requirements that will help you implement the comparison:

  1. class A is subclass of class B
  2. class A implements the interface B

Both of these can be checked by using the method Java provides in Class called isAssignableFrom.

c1.isAssignableFrom(c2)

is true if c1 "is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter" (ie c2) - so basically c1.isSuperclassOf(c2). For your comparison, that means, if it returns true, c1 < c2.

So let's use this to write the Comparator.

public HierarchyComparator implements Comparator<Class<?>> {
    public int compare(Class<?> c1, Class<?> c2) {
        int result;
        // we need to do this check because isAssignableFrom is true in this case
        // and we would get an order that doesn't exist
        if (c1.equals(c2)) {
            return 0;
        }
        if (c1.isAssignableFrom(c2)) {
            return -1;
        } else if (c2.isAssignableFrom(c1)) {
            return 1;
        }
        // no hierarchy
        return 0;
    }
}

Then, you can sort classes by

public List<Class<?>> sort(Class<?>... classes) {
    List<Class<?>> result = new ArrayList<>(Arrays.asList(classes));
    Collections.sort(result, new HierarchyComparator());
}
daniu
  • 14,137
  • 4
  • 32
  • 53
  • 1
    This is wrong. This `Comparator` does not specify total order on classes [as required](https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html), because it's not antisymmetric. So, `Collections.sort` won't work. – yeputons Jun 19 '18 at 12:20
  • @yeputons antisymmetricity is not one of the requirements of `Comparator#compare`, read its documentation. It requires "sgn(compare(x, y)) == -sgn(compare(y, x))" which is fulfilled here, and transitivity, which is as well. Do provide a counter sample in case I miss something. – daniu Jun 20 '18 at 06:29
  • sorry, I should've used rigid quote instead of some implicit hand-waving: it's required that if `compare(x, y) == 0`, then `x` and `y` behave in the same way when compared with arbitrary `z` ([link](https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html#compare(T,%20T))). That's not true: consider `B extends A` and independent `C`. Then `compare(B, C) == 0`, but `compare(B, A) == 1` and `compare(C, A) == 0`. – yeputons Jun 20 '18 at 09:57
  • @yeputons Hm, you are correct, that requirement does exist... I wasn't aware of that. So, strictly speaking, my answer isn't correct. However, in practice, this isn't a real problem; it only means that two calls to `Collections.sort` with this `Comparator` will yield different result. For the given problem, this is sufficient: the result would be either `A-B, C` or `C, A-B` with `A-B` being the required hierarchy sorting. – daniu Jun 20 '18 at 12:55
  • 1
    no, it means that your answer violates contract of `Comparator` which may result in _any arbitrary result_, including, say, [throwing exceptions](https://stackoverflow.com/questions/19182700/collections-sort-throws-comparison-method-violates-its-general-contract-excep). For example, `B,C,A,` is valid sorting result: `compare` of any two adjacent elements is zero. So `Collection.sort` is allowed to [not modify such list at all](https://ideone.com/1wyNLh). – yeputons Jun 20 '18 at 15:27
  • @yeputons yes, it did break the contract and it's allowed to throw exceptions, but again in the real world won't. The issue in the past you linked was caused by a NaN in the double list being sorted, not something that will happen here. I add a bit that it shouldn't be used on its own when sorting though, and that users will need to provide a proper secondary sort. Which would be impossible if it were established here beyond what's actually required. – daniu Jun 20 '18 at 16:03
  • 1
    [Here](https://ideone.com/5f1bQf) is an example of using your comparator to sort a bunch of direct subclasses of `Exception` together with `Exception` itself. It fails. Do you have any specific limitations on "real world usage" or a suggestion on any kind of "a proper secondary sort" that may fix the problem? – yeputons Jun 20 '18 at 16:49
  • @yeputons sure, `Comparator.comparing(Class::get Name)`. Am on mobile right now so I can't try it out, but I'm sure you can connect the two by `Comparator#thenComparing` to fulfill the contract. – daniu Jun 20 '18 at 17:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173500/discussion-between-yeputons-and-daniu). – yeputons Jun 20 '18 at 18:19
  • @yeputons Sorry, I never have time for chat... actually, the Exception in the sample you posted stemmed from a typo in my answer (fixed now); it should be `else if (c2.isAssignableFrom(c1))` rather than `else if (c2.isAssignableFrom(c2))`. It doesn't give the exception now, and it's the real world example you asked for. – daniu Jun 21 '18 at 08:48
  • but it still doesn't solve the initial problem, even with a simpler set of classes. I've added answer correctness check, see [here](https://ideone.com/vGYRyQ) (note that the bug is fixed). – yeputons Jun 21 '18 at 15:34
0

Hope this helps. Class PrintClassHierarchy needs the input of classes which you want to print in hierarchy order.

//sample classes for tutorial
class A{

}

class B extends A {

}


class C extends A {

}


class D extends C {

}
class E extends B {

}
    class F extends B {

    }

//print hierarchy
    public class PrintClassHierarchy {
        private static final String PADDING = "        ";
        private static final String PADDING_WITH_COLUMN = "   |    ";
        private static final String PADDING_WITH_ENTRY = "   |--- ";
        private static final String BASE_CLASS = Object.class.getName();

        private final Map<String, List<String>> subClazzEntries = new HashMap<>();

        public static void main(final String[] args) {
            new PrintClassHierarchy(
                A.class,
                B.class,
                C.class,
                D.class,
                E.class,
                F.class
            ).printHierarchy();
        }

        public PrintClassHierarchy(final Class<?>... clazzes) {
            // get all entries of tree
            traverseClasses(clazzes);
        }

        public void printHierarchy() {
            // print collected entries as ASCII tree
            printHierarchy(BASE_CLASS, new Stack<Boolean>());
        }

        private void printHierarchy(final String clazzName, final Stack<Boolean> moreClassesInHierarchy) {
            if (!moreClassesInHierarchy.empty()) {
                for (final Boolean hasColumn : moreClassesInHierarchy.subList(0, moreClassesInHierarchy.size() - 1)) {
                    System.out.print(hasColumn.booleanValue() ? PADDING_WITH_COLUMN : PADDING);
                }
            }

            if (!moreClassesInHierarchy.empty()) {
                System.out.print(PADDING_WITH_ENTRY);
            }

            System.out.println(clazzName);

            if (subClazzEntries.containsKey(clazzName)) {
                final List<String> list = subClazzEntries.get(clazzName);

                for (int i = 0; i < list.size(); i++) {
                    // if there is another class that comes beneath the next class, flag this level
                    moreClassesInHierarchy.push(new Boolean(i < list.size() - 1));

                printHierarchy(list.get(i), moreClassesInHierarchy);

                moreClassesInHierarchy.removeElementAt(moreClassesInHierarchy.size() - 1);
            }
        }
    }

    private void traverseClasses(final Class<?>... clazzes) {
        // do the traverseClasses on each provided class (possible since Java 8)
        Arrays.asList(clazzes).forEach(c -> traverseClasses(c, 0));
    }

    private void traverseClasses(final Class<?> clazz, final int level) {
        final Class<?> superClazz = clazz.getSuperclass();

        if (superClazz == null) {
            // we arrived java.lang.Object
            return;
        }

        final String name = clazz.getName();
        final String superName = superClazz.getName();

        if (subClazzEntries.containsKey(superName)) {
            final List<String> list = subClazzEntries.get(superName);

            if (!list.contains(name)) {
                list.add(name);
                Collections.sort(list); // SortedList
            }
        } else {
            subClazzEntries.put(superName, new ArrayList<String>(Arrays.asList(name)));
        }

        traverseClasses(superClazz, level + 1);
    }
}

OUTPUT:

java.lang.Object
       |--- A
               |--- B
               |       |--- E
               |       |--- F
               |--- C
                       |--- D
Rajat Khandelwal
  • 477
  • 1
  • 5
  • 19
-1

You can do this using constructor.

List<String> listOfClasses = new ArrayList<>();
public class MyClass{
   public MyClass(){
      listOfClasses.add(this.getClass().getSimpleName());
   }
}
public class SubClass extends MyClass{
   public SubClass(){
       // here, first statement super() which calls constructor of superclass
       listOfClasses.add(this.getClass().getSimpleName());
   } 
}

So,when you create object in subclass, then constructor of all classes are called and these are stored in listOfClasses in sorted way.

Amit Kumar
  • 377
  • 4
  • 17
  • How does this sort a list of classes? – daniu Jun 19 '18 at 06:38
  • Use any global variable, like list of string and call `listOfstring.add(className)` in every constructor. Then listOfString contains all classes in sorted order. – Amit Kumar Jun 19 '18 at 06:41