Как вы находите все подклассы данного класса в Java?
Как вы идете и пытаетесь найти все подклассы данного класса (или все разработчики данного интерфейса) в Java?
На данный момент у меня есть способ сделать это, но я считаю его довольно неэффективным (по меньшей мере).
Метод:
- Получить список всех имен классов, существующих на пути к классу
- Загрузите каждый класс и проверьте, является ли он подклассом или разработчиком желаемого класса или интерфейса
В Eclipse есть хорошая функция, называемая иерархией типов, которой удается показать это достаточно эффективно.
Как это происходит и делать это программно?
Ответы
Ответ 1
Нет другого способа сделать это, кроме того, что вы описали. Подумайте об этом - как кто-нибудь может узнать, какие классы расширяют ClassX, не просматривая каждый класс в пути к классам?
Eclipse может только рассказать вам о супер и подклассах в том, что кажется "эффективным" временем, потому что у него уже есть все данные типа, загруженные в тот момент, когда вы нажимаете кнопку "Отображать в иерархии типов" ( так как он постоянно компилирует ваши классы, знает обо всем на пути к классам и т.д.).
Ответ 2
Сканирование для классов непросто с чистой Java.
Структура spring предлагает класс под названием ClassPathScanningCandidateComponentProvider, который может делать то, что вам нужно. В следующем примере будут найдены все подклассы MyClass в пакете org.example.package
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider (false);
provider.addIncludeFilter(новый AssignableTypeFilter (MyClass.class));
// сканирование в org.example.package
Set <BeanDefinition> components = provider.findCandidateComponents( "org/example/package" );
для компонента BeanDefinition: компоненты)
{ Класс cls = Class.forName(component.getBeanClassName()); // используем класс cls found
}
Код>
Этот метод имеет дополнительное преимущество использования анализатора байт-кода для поиска кандидатов, что означает, что он не загружает все классы, которые он сканирует.
Ответ 3
Это невозможно сделать, используя только встроенный API Java Reflections.
Существует проект, который выполняет необходимое сканирование и индексирование вашего пути к классам, чтобы вы могли получить доступ к этой информации...
Анализ метаданных времени выполнения Java в духе Scannotations
Reflections сканирует ваш путь к классам, индексирует метаданные, позволяет запрашивать его во время выполнения и может сохранять и собирать эту информацию для многих модулей вашего проекта.
Используя Reflections, вы можете запросить свои метаданные для:
- получить все подтипы некоторого типа
- получить все типы, аннотированные с помощью некоторой аннотации
- получить все типы, аннотированные с некоторой аннотацией, включая параметры аннотации, соответствующие
- получить все методы, аннотированные с помощью
(отказ от ответственности: я не использовал его, но описание проекта, по-видимому, соответствует вашим потребностям.)
Ответ 4
Не забывайте, что сгенерированный Javadoc для класса будет включать список известных подклассов (и для интерфейсов, известных классов реализации).
Ответ 5
Я сделал это несколько лет назад. Самый надежный способ сделать это (например, с официальными API Java и без внешних зависимостей) - написать пользовательский документ, чтобы создать список, который можно прочитать во время выполнения.
Вы можете запустить его из командной строки следующим образом:
javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example
или запустите его из ant следующим образом:
<javadoc sourcepath="${src}" packagenames="*" >
<doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>
Вот базовый код:
public final class ObjectListDoclet {
public static final String TOP_CLASS_NAME = "com.example.MyClass";
/** Doclet entry point. */
public static boolean start(RootDoc root) throws Exception {
try {
ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
for (ClassDoc classDoc : root.classes()) {
if (classDoc.subclassOf(topClassDoc)) {
System.out.println(classDoc);
}
}
return true;
}
catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
}
Для простоты я удалил разбор аргументов командной строки, и я пишу в System.out, а не в файл.
Ответ 6
Я знаю, что на несколько лет опаздываю на эту вечеринку, но я столкнулся с этим вопросом, пытаясь решить ту же проблему. Вы можете использовать внутренний поиск Eclipse программно, если вы пишете плагин Eclipse (и, таким образом, используете их кеширование и т.д.), Чтобы найти классы, которые реализуют интерфейс. Здесь мой (очень грубый) первый разрез:
protected void listImplementingClasses( String iface ) throws CoreException
{
final IJavaProject project = <get your project here>;
try
{
final IType ifaceType = project.findType( iface );
final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
final SearchEngine searchEngine = new SearchEngine();
final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
searchEngine.search( ifacePattern,
new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {
@Override
public void acceptSearchMatch( SearchMatch match ) throws CoreException
{
results.add( match );
}
}, new IProgressMonitor() {
@Override
public void beginTask( String name, int totalWork )
{
}
@Override
public void done()
{
System.out.println( results );
}
@Override
public void internalWorked( double work )
{
}
@Override
public boolean isCanceled()
{
return false;
}
@Override
public void setCanceled( boolean value )
{
}
@Override
public void setTaskName( String name )
{
}
@Override
public void subTask( String name )
{
}
@Override
public void worked( int work )
{
}
});
} catch( JavaModelException e )
{
e.printStackTrace();
}
}
Первой проблемой, которую я вижу до сих пор, является то, что я только улавливаю классы, которые непосредственно реализуют интерфейс, а не все их подклассы, но небольшая рекурсия никогда никому не повредит.
Ответ 7
Имея в виду ограничения, упомянутые в других ответах, вы также можете использовать openpojo PojoClassFactory
(доступный на Maven) следующим образом:
for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(packageRoot, Superclass.class, null)) {
System.out.println(pojoClass.getClazz());
}
Где packageRoot
- это корневая строка пакетов, которые вы хотите искать (например, "com.mycompany"
или даже просто "com"
), а Superclass
- ваш супертип (это также работает и с интерфейсами).
Ответ 8
Следует также отметить, что это, конечно же, найдет только те подклассы, которые существуют в вашем текущем пути к классам. Предположительно, это нормально для того, что вы сейчас просматриваете, и, скорее всего, вы это рассмотрели, но если в какой-то момент вы выпустили класс не final
в дикий (для разных уровней "дикий" ), то это вполне возможно, что кто-то другой написал свой собственный подкласс, о котором вы не узнаете.
Таким образом, если вам захотелось увидеть все подклассы, потому что вы хотите внести изменения и увидите, как они влияют на поведение подклассов, тогда имейте в виду подклассы, которые вы не видите. В идеале все ваши не-частные методы и сам класс должны быть хорошо документированы; внесите изменения в соответствии с этой документацией, не изменяя семантику методов/нефизических полей, и ваши изменения должны быть совместимы с обратным доступом для любого подкласса, который по крайней мере соответствовал вашему определению суперкласса.
Ответ 9
Причина, по которой вы видите разницу между вашей реализацией и Eclipse, заключается в том, что вы просматриваете каждый раз, в то время как Eclipse (и другие инструменты) сканируют только один раз (во время загрузки проекта большую часть времени) и создают индекс. В следующий раз, когда вы попросите данные, которые он не сканирует снова, но посмотрите на индекс.
Ответ 10
Попробуйте ClassGraph. (Отказ от ответственности, я автор). ClassGraph поддерживает сканирование для подклассов данного класса либо во время выполнения, либо во время сборки, но и многое другое. ClassGraph может создавать абстрактное представление всего графа классов (все классы, аннотации, методы, параметры метода и поля) в памяти, для всех классов в пути к классам или для классов в белых списках, и вы можете запросить этот графа классов, однако вы хотите. ClassGraph поддерживает больше механизмов спецификации классов и классов, чем любой другой сканер, а также прекрасно работает с новой системой модулей JPMS, поэтому, если вы основываете свой код на ClassGraph, ваш код будет максимально переносимым. Смотрите здесь API.
Ответ 11
Я просто пишу простую демонстрацию, чтобы использовать org.reflections.Reflections
для получения подклассов абстрактного класса:
https://github.com/xmeng1/ReflectionsDemo
Ответ 12
Я использую отражение lib, которое сканирует ваш путь к классам для всех подклассов: https://github.com/ronmamo/reflections
Вот как это было бы сделано:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
Ответ 13
В зависимости от ваших конкретных требований, в некоторых случаях механизм Java service loader может достичь того, что вам нужно.
Вкратце, он позволяет разработчикам явно объявлять, что класс подклассифицирует какой-либо другой класс (или реализует некоторый интерфейс), перечислив его в файл в каталоге JAR/WAR META-INF/services
. Затем он может быть обнаружен с помощью класса java.util.ServiceLoader
, который при задании объекта Class
будет генерировать экземпляры всех объявленных подклассов этот класс (или, если Class
представляет собой интерфейс, все классы, реализующие этот интерфейс).
Основное преимущество этого подхода состоит в том, что нет необходимости вручную проверять весь путь к классам для подклассов - вся логика обнаружения содержится в классе ServiceLoader
и загружает только классы, явно объявленные в META-INF/services
каталог (не каждый класс в пути к классам).
Существуют, однако, некоторые недостатки:
- Он не найдет подклассы all, только те, которые явно объявлены. Таким образом, если вам действительно нужно найти все подклассы, этот подход может быть недостаточным.
- Это требует, чтобы разработчик явно объявлял класс в каталоге
META-INF/services
. Это дополнительное бремя для разработчика и может быть подвержено ошибкам.
-
ServiceLoader.iterator()
генерирует экземпляры подкласса, а не их объекты Class
. Это вызывает два вопроса:
- Вы не можете сказать, как строятся подклассы - конструктор no-arg используется для создания экземпляров.
- Таким образом, подклассы должны иметь конструктор по умолчанию, или должны быть объяснены конструктором no-arg.
Очевидно, что Java 9 будет решать некоторые из этих недостатков (в частности, те, которые касаются создания подклассов).
Пример
Предположим, вы заинтересованы в поиске классов, реализующих интерфейс com.example.Example
:
package com.example;
public interface Example {
public String getStr();
}
Класс com.example.ExampleImpl
реализует этот интерфейс:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl string.";
}
}
Вы объявили бы, что класс ExampleImpl
является реализацией Example
, создав файл META-INF/services/com.example.Example
, содержащий текст com.example.ExampleImpl
.
Затем вы можете получить экземпляр каждой реализации Example
(включая экземпляр ExampleImpl
) следующим образом:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl string.", plus whatever is returned
// by other declared implementations of com.example.Example.
Ответ 14
Добавьте их в статическую карту внутри (this.getClass(). getName()) конструктор родительских классов (или создайте по умолчанию), но это будет обновляться во время выполнения. Если ленивая инициализация - это вариант, вы можете попробовать этот подход.
Ответ 15
Мне нужно было сделать это в качестве тестового примера, чтобы узнать, добавлены ли новые классы в код. Это то, что я сделал
final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
private static ArrayList<String> files = new ArrayList<String>();
listFilesForFolder(rootFolder);
@Test(timeout = 1000)
public void testNumberOfSubclasses(){
ArrayList<String> listSubclasses = new ArrayList<>(files);
listSubclasses.removeIf(s -> !s.contains("Superclass.class"));
for(String subclass : listSubclasses){
System.out.println(subclass);
}
assertTrue("You did not create a new subclass!", listSubclasses.size() >1);
}
public static void listFilesForFolder(final File folder) {
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
listFilesForFolder(fileEntry);
} else {
files.add(fileEntry.getName().toString());
}
}
}