Страницы

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

пятница, 13 декабря 2019 г.

Cоздать экземпляры всех классов, которые наследуются от интерфейса. Java

#java #наследование #интерфейс


Например, есть интерфейс "Квартира".
Нужно пройтись по всем типам квартир, которые наследуют интерфейс "Квартира", (одно-,
двух-, трех- и т.д.) и, скажем, добавить их в ArrayList.

Можно ли такое сделать? Если да, то как?
    


Ответы

Ответ 1



Это реализуемо. Это даже может быть полезно, когда вы пишите какой-нибудь расширяемый фреймворк и заранее не знаете, с какими объектами придется работать, а заставлять пользователя регистрировать классы вручную (как это происходит, например, в JDBC) почему-то неприемлимо. В общем случае вам нужно: Выполнить сканирование CLASSPATH Проверить все найденные классы через рефлексию на наследование интерфейса/класса. Вызвать через рефлексию конструктор. Сразу стоит понять: либо вы точно знаете (требуете), какой конструктор будет реализован во всех классах, либо используете дефолтный конструктор без параметров (а он создается автоматически, только если нет других явных конструкторов). Пункты 1 и 2 несколько утомительны для реализации вручную. К счастью есть отличная библиотека Reflections, которая позволит сделать это двумя строками кода. Допустим, нас интересуют все реализации интерфейса HttpHandler: Reflections reflections = new Reflections(); Set> classes = reflections.getSubTypesOf(HttpHandler.class); И все! Теперь осталось перебрать все экземпляры Class и вызвать, допустим конструктор без параметров: List handlers = new ArrayList<>(); for (Class clazz : classes) { try { handlers.add(clazz.newInstance()); } catch (InstantiationException | IllegalAccessException e) { System.out.println("Не удалось создать экземпляр: " + clazz.getCanonicalName()); } } Но! Если вы еще не пишете свои фреймворки, то есть вероятность, что вы как-то неправильно, ломая ООП, пытаетесь решить совсем иную задачу. Альтернативные реализации сканера CLASSPATH: https://github.com/lukehutch/fast-classpath-scanner

Ответ 2



Следующий метод находит все классы находящийся в указанном jar архиве реализующие определенный интерфейс: Collection> getAllInterfaceImplementations(String pathToJar, Class interfaceType) throws Exception { JarFile jarFile = new JarFile(pathToJar); Enumeration e = jarFile.entries(); URL[] urls = {new URL("jar:file:" + pathToJar + "!/")}; URLClassLoader cl = URLClassLoader.newInstance(urls); Collection> classes = new ArrayList<>(); while (e.hasMoreElements()) { JarEntry je = e.nextElement(); if (je.isDirectory() || !je.getName().endsWith(".class")) continue; String className = je.getName().substring(0, je.getName().length() - ".class".length()); className = className.replace('/', '.'); Class type = cl.loadClass(className); if (interfaceType.isAssignableFrom(type)) classes.add(type); } return classes; } Использовать вот так: String pathToJar = "<путь до JAVA_HOME>/rt.jar"; for (Class type : getAllInterfaceImplementations(pathToJar, Collection.class)) System.out.println(type.getName());

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

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