Я хочу сделать что-то вроде этого:
List<Animal> animals = new ArrayList<Animal>();
for( Class c: list_of_all_classes_available_to_my_app() )
if (c is Animal)
animals.add( new c() );
Итак, я хочу посмотреть на все классы во вселенной моего приложения, и когда я найду тот, который происходит от Animal, я хочу создать новый объект этого типа и добавить его в список. Это позволяет мне добавлять функциональность, не обновляя список вещей. Я могу избежать следующего:
List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
С помощью описанного выше подхода я могу просто создать новый класс, расширяющий Animal, и он будет автоматически выбран.
ОБНОВЛЕНИЕ: 16.10.2008 9:00 по тихоокеанскому стандартному времени:
Этот вопрос вызвал много хороших ответов - спасибо. Из ответов и моих исследований я обнаружил, что то, что я действительно хочу сделать, просто невозможно в Java. Есть подходы, такие как механизм ServiceLoader ddimitrov, который может работать - но они очень тяжелы для того, что я хочу, и я считаю, что я просто перенесу проблему из кода Java во внешний файл конфигурации. Обновление 5/10/19 (11 лет спустя!) Есть теперь несколько библиотек , которые могут помочь с этим в соответствии с @ IvanNik в ответ org.reflections выглядит хорошо. Также ClassGraph от @Luke Хатчисон ответа выглядит интересно. В ответах есть еще несколько возможностей.
Другой способ заявить, что я хочу: статическая функция в моем классе Animal находит и создает все классы, которые наследуются от Animal - без какой-либо дальнейшей настройки / кодирования. Если мне нужно настроить, я все равно могу просто создать их экземпляр в классе Animal. Я понимаю это, потому что Java-программа - это просто свободная федерация файлов .class, и это именно так.
Интересно, что в C # это довольно тривиально .
источник
Ответы:
Я использую org.reflections :
Другой пример:
источник
animals.add( new c() );
вашего кода), потому что, покаClass.newInstance()
существует, у вас нет гарантии, что конкретный класс имеет конструктор без параметров (C # позволяет вам требовать этого в универсальном)Java способ сделать то, что вы хотите, это использовать механизм ServiceLoader .
Также многие люди свертывают свои собственные, имея файл в хорошо известном месте пути к классам (например, /META-INF/services/myplugin.properties), а затем используя ClassLoader.getResources () для перечисления всех файлов с этим именем из всех jar- файлов. Это позволяет каждому банку экспортировать своих собственных провайдеров, и вы можете создавать их экземпляры путем отражения с помощью Class.forName ()
источник
Подумайте об этом с аспектно-ориентированной точки зрения; на самом деле вы хотите знать все классы во время выполнения, которые расширили класс Animal. (Я думаю, что это немного более точное описание вашей проблемы, чем ваше название; в противном случае, я не думаю, что у вас есть вопрос времени выполнения.)
Поэтому я думаю, что вы хотите создать конструктор вашего базового класса (Animal), который добавляет к вашему статическому массиву (я сам предпочитаю ArrayLists, но каждому из них) тип текущего класса, для которого создается экземпляр.
Итак, примерно;
Конечно, вам понадобится статический конструктор в Animal для инициализации instantiatedDerivedClass ... Я думаю, что это будет делать то, что вы, вероятно, хотите. Обратите внимание, что это зависит от пути выполнения; если у вас есть класс Dog, производный от Animal, который никогда не вызывается, его не будет в вашем списке классов Animal.
источник
К сожалению, это не совсем возможно, так как ClassLoader не скажет вам, какие классы доступны. Вы можете, однако, быть довольно близко, делая что-то вроде этого:
Изменить: johnstok правильно (в комментариях), что это работает только для автономных приложений Java, и не будет работать под сервером приложений.
источник
URL[]
но не всегда, так что это может быть невозможно) из иерархии ClassLoader. Часто, хотя у вас должно быть разрешение, поскольку обычно выSecurityManager
загружаете его в JVM.Вы можете использовать ResolverUtil ( необработанный источник ) из Stripes Framework
если вам нужно что-то простое и быстрое без рефакторинга какого-либо существующего кода.
Вот простой пример, не загружающий ни один из классов:
Это также работает на сервере приложений, так как именно там он был разработан для работы;)
Код в основном делает следующее:
ClassLoader#loadClass(String fullyQualifiedName)
Animal.class.isAssignableFrom(loadedClass);
источник
Наиболее надежным механизмом для перечисления всех подклассов данного класса в настоящее время является ClassGraph , поскольку он обрабатывает максимально широкий массив механизмов спецификации пути к классам , включая новую систему модулей JPMS. (Я автор.)
источник
Java динамически загружает классы, поэтому ваша вселенная классов будет только теми, которые уже загружены (и еще не выгружены). Возможно, вы можете сделать что-то с пользовательским загрузчиком классов, который может проверять супертипы каждого загруженного класса. Я не думаю, что есть API для запроса набора загруженных классов.
источник
использовать это
Редактировать:
источник
Спасибо всем, кто ответил на этот вопрос.
Кажется, это действительно крепкий орешек. В итоге я сдался и создал статический массив и геттер в своем базовом классе.
Кажется, что Java просто не настроен на самообнаружение, как C #. Я предполагаю, что проблема заключается в том, что, поскольку приложение Java представляет собой просто набор файлов .class в каком-либо каталоге / jar-файле, среда выполнения не знает о классе, пока на него не ссылаются. В это время загрузчик загружает его - я пытаюсь найти его, прежде чем ссылаться на него, что невозможно без обращения к файловой системе и поиска.
Мне всегда нравится код, который может обнаружить себя, а не рассказывать о себе, но, увы, это тоже работает.
Еще раз спасибо!
источник
Используя OpenPojo вы можете сделать следующее:
источник
Это сложная проблема, и вам нужно будет найти эту информацию с помощью статического анализа, которая не может быть легко доступна во время выполнения. По сути, получите путь к классу вашего приложения, просмотрите доступные классы и прочитайте информацию о байт-коде класса, от которого он наследует класс. Обратите внимание, что класс Dog не может наследовать напрямую от Animal, но может наследовать от Pet, который в свою очередь наследует от Animal, поэтому вам нужно будет отслеживать эту иерархию.
источник
Один из способов - заставить классы использовать статические инициализаторы ... Я не думаю, что они наследуются (это не сработает):
Требуется добавить этот код ко всем участвующим классам. Но это позволяет избежать больших уродливых циклов, где каждый класс ищет детей Animal.
источник
Я решил эту проблему довольно элегантно, используя аннотации уровня пакета, а затем сделал эту аннотацию в качестве аргумента списком классов.
Найти классы Java, реализующие интерфейс
Реализации просто должны создать package-info.java и поместить волшебную аннотацию со списком классов, которые они хотят поддерживать.
источник