Страницы

Поиск по вопросам

вторник, 27 ноября 2018 г.

java Как найти всех наследников класса в пакете?

Казалось бы ответ очевиден:
(new Reflections("package")).getSubTypesOf(SuperClass.class)
Однако эта конструкция находит не всех наследников, в случае, если иерархия разнесена на несколько пакетов.
Есть следующие классы:
package1.ParentClass extemds SuperClass package2.ChildClass extends ParentClass
Пакеты package1 и package2 не вложенные.
Конструкция
(new Reflections("package1")).getSubTypesOf(SuperClass.class)
Ожидаемо вернёт package1.ParentClass
А вот конструкция
(new Reflections("package2")).getSubTypesOf(SuperClass.class)
Наш package2.ChildClass не вернёт, хотя это очевидный наследник SuperClass
Я понимаю. что могу в ручную пробежаться по всем классам пакета и собрать нужные. Но неужели нет стандартного способа?
И ещё, объясните, почему по умолчанию ChildClass не находится? Для меня это было очень неожиданное поведение от org.reflections.Reflections
UPD: Изучил исходники ядра org.reflections, всё стало на свои места. Если кратко, java именно так и реализует Reflections. У каждого класса в рефлексии есть только ссылка на родителя. Поэтому поиск подклассов в любом случае перебор. Почему в этом переборе нет супер классов из иных пакетов - вопрос отдельный. Так реализовано.
Для каждой конкретной задачи нужно использовать оптимальное решение. Но хотелось бы услышать о уже существующих решениях, которые позволяют найти всех потомков, кроме java org.reflections.
UPD: Ещё один интересный и нетрудоёмкий способ.
Reflections reflections = new Reflections("package1", new SubTypesScanner()) Set> subTypes = new HashSet<>(); for (String className : reflections.getStore().get(SubTypesScanner.class.getSimpleName()).values()) { try { Class subType = Class.forName(className); if (SuperClass.class.isAssignableFrom(subType)) { subTypes.add(subType); } } catch (ClassNotFoundException e) { throw new RuntimeException("Этого не может быть:)", e); } } return subTypes;
Данное решение перебирает все классы пакета (кроме наследников object)
reflections.getStore().get(SubTypesScanner.class.getSimpleName()).values()
и проверяет на то, что они являются наследником или реализуют SuperClass
SuperClass.class.isAssignableFrom(subType)
По производительности, по крайней мере, лучше, чем поиск по всем пакетам и последующая фильтрация


Ответ

И ещё, объясните, почему по умолчанию ChildClass не находится?
Причина в том, что класс ChildClass является не прямым наследником SuperClass, а через класс ParentClass.
Reflections не уходит рекурсивно на проверку "родителей-родителей" и так далее, поэтому для обнаружения факта наследования через "промежуточные" классы необходимо загрузить пакеты со всеми "промежуточными" классами. В данном случае для того чтобы определить факт наследования ChildClass от SuperClass необходимо так же загрузить ParentClass. Лишние классы из полученного множества(Set) можно отфильтровать по имени пакета.
(new Reflections("package1","package2")) .getSubTypesOf(SuperClass.class) .stream() .filter(c->c.getPackage().getName().equals("package2")).collect(Collectors.toSet());

1) Если неизвестен пакет "package1" то можно загрузить реализации из всех пакетов и потом отфильтровать по нужному пакету "package2":
Set> s = new Reflections( ClasspathHelper.forClass(SuperClass.class)) .getSubTypesOf(SuperClass.class) .stream() .filter(c->c.getPackage().getName().equals("package2")) .collect(Collectors.toSet());
или
Set> s = new Reflections( new ConfigurationBuilder() .setUrls(ClasspathHelper.forClass(SuperClass.class)) .filterInputsBy(new FilterBuilder().excludePackage("package2"))) .getSubTypesOf(SuperClass.class);
2) В качестве более производительной альтернативы можно воспользоваться стандартными средствами “java core” и получить все файлы-классы пакета и их уже рекурсивно проверить на наличие искомого наследника.
public static Set> loadClasses(String packageName) throws ClassNotFoundException { Set> classes = new HashSet<>(); URL resource = Thread.currentThread() .getContextClassLoader() .getResource(packageName.replace('.', '/')); File directory = new File(resource.getFile()); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); if (files == null || files.length == 0) { return classes; } for (File file : files) { //в другие пакеты уходить ненужно if (file.isFile() && file.getName().endsWith(".class")) { classes.add(Class .forName(String.format("%s.%s", packageName, file.getName().substring(0, file.getName().indexOf("."))))); } } return classes; }

public static boolean checkAllParents(Class type, Class hasParentType) { Class parent = type.getSuperclass(); if (parent == Object.class) { return false; } if (parent.getClass() == hasParentType.getClass()) { return true; } return checkAllParents(parent, hasParentType); }

Set> s = loadClasses("package2").stream() .filter(i->checkAllParents(i, SuperClass.class)) .collect(Collectors.toSet());

Комментариев нет:

Отправить комментарий