Как вы находите все подклассы данного класса в Java? - PullRequest
190 голосов
/ 29 января 2009

Как можно найти и попытаться найти все подклассы данного класса (или всех разработчиков данного интерфейса) в Java? На данный момент у меня есть способ сделать это, но я нахожу его довольно неэффективным (если не сказать больше). Метод:

  1. Получить список всех имен классов, существующих в пути к классам
  2. Загрузить каждый класс и проверить, является ли он подклассом или разработчиком нужного класса или интерфейса

В Eclipse есть замечательная функция, которая называется Type Hierarchy, которая позволяет показать это довольно эффективно. Как это сделать и сделать это программно?

Ответы [ 15 ]

3 голосов
/ 29 января 2009

Следует также отметить, что, конечно, будут найдены только те подклассы, которые существуют на вашем текущем пути к классам. Предположительно, это нормально для того, на что вы сейчас смотрите, и есть вероятность, что вы действительно обдумали это, но если в какой-то момент вы выпустили не-1001 * класс в дикую природу (для разных уровней «дикого»), тогда вполне возможно, что кто-то другой написал свой собственный подкласс, о котором вы не будете знать.

Таким образом, если вам захотелось увидеть все подклассы, потому что вы хотите внести изменения и увидите, как это влияет на поведение подклассов, - имейте в виду подклассы, которые вы не видите. В идеале все ваши не приватные методы и сам класс должны быть хорошо документированы; вносите изменения в соответствии с этой документацией, не изменяя семантику методов / непубличных полей, и ваши изменения должны быть обратно совместимы для любого подкласса, который, по крайней мере, следовал вашему определению суперкласса.

2 голосов
/ 10 августа 2017

В зависимости от ваших конкретных требований, в некоторых случаях механизм загрузки сервиса Java может достичь того, что вам нужно.

Короче говоря, он позволяет разработчикам явно объявить, что класс подклассов какого-то другого класса (или реализует некоторый интерфейс), перечислив его в файл в каталоге META-INF/services файла JAR / WAR. Затем его можно обнаружить с помощью класса java.util.ServiceLoader, который при задании объекта Class будет генерировать экземпляры всех объявленных подклассов этого класса (или, если Class представляет интерфейс, все классы, реализующие этот интерфейс).

Основным преимуществом этого подхода является то, что нет необходимости вручную сканировать весь путь к классам для подклассов - вся логика обнаружения содержится в классе ServiceLoader, и он загружает только классы, явно объявленные в META-INF/services каталог (не каждый класс на пути к классам).

Есть, однако, некоторые недостатки:

  • Он не найдет всех подклассов, только те, которые явно объявлены. Таким образом, если вам нужно действительно найти все подклассы, такой подход может оказаться недостаточным.
  • Требуется, чтобы разработчик явно объявил класс в каталоге META-INF/services. Это является дополнительным бременем для разработчика и может быть подвержено ошибкам.
  • ServiceLoader.iterator() генерирует экземпляры подкласса, а не их Class объекты. Это вызывает две проблемы:
    • Вы не можете сказать, как создаются подклассы - конструктор 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's 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's string.", plus whatever is returned
// by other declared implementations of com.example.Example.
2 голосов
/ 03 августа 2017

Я использую библиотеку отражений, которая сканирует ваш путь к классам для всех подклассов: https://github.com/ronmamo/reflections

Вот как это будет сделано:

Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
1 голос
/ 14 апреля 2015

Добавьте их в статическую карту внутри (this.getClass (). GetName ()) конструктора родительских классов (или создайте конструктор по умолчанию), но он будет обновляться во время выполнения. Если ленивая инициализация является опцией, вы можете попробовать этот подход.

0 голосов
/ 20 апреля 2018

Мне нужно было сделать это в качестве контрольного примера, чтобы увидеть, были ли добавлены новые классы в код. Это то, что я сделал

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());
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...