¿Cómo se puede tratar de encontrar todas las subclases de una clase determinada (o todos los implementadores de una interfaz determinada) en Java? A partir de ahora, tengo un método para hacer esto, pero lo encuentro bastante ineficiente (por decir lo menos). El metodo es:
- Obtenga una lista de todos los nombres de clase que existen en la ruta de clase
- Cargue cada clase y pruebe para ver si es una subclase o implementador de la clase o interfaz deseada
En Eclipse, hay una buena característica llamada Jerarquía de tipos que logra mostrar esto de manera bastante eficiente. ¿Cómo se hace y se hace programáticamente?
Respuestas:
No hay otra forma de hacerlo que no sea lo que describiste. Piénselo: ¿cómo puede alguien saber qué clases extienden ClassX sin escanear cada clase en el classpath?
Eclipse solo puede informarle sobre las superclases y subclases en lo que parece ser una cantidad de tiempo "eficiente" porque ya tiene todos los datos de tipo cargados en el punto donde presiona el botón "Mostrar en la jerarquía de tipos" (ya que es compila constantemente tus clases, sabe todo sobre el classpath, etc.
fuente
reflections.getSubTypesOf(aClazz))
linkEscanear en busca de clases no es fácil con Java puro.
Spring Framework ofrece una clase llamada ClassPathScanningCandidateComponentProvider que puede hacer lo que necesita. El siguiente ejemplo encontraría todas las subclases de MyClass en el paquete org.example.package
Este método tiene la ventaja adicional de usar un analizador de código de bytes para encontrar los candidatos, lo que significa que no cargará todas las clases que escanea.
fuente
Esto no es posible hacerlo utilizando solo la API Java Reflections incorporada.
Existe un proyecto que realiza el escaneo e indexación necesarios de su classpath para que pueda acceder a esta información ...
Reflexiones
(descargo de responsabilidad: no lo he usado, pero la descripción del proyecto parece ajustarse exactamente a sus necesidades).
fuente
No olvide que el Javadoc generado para una clase incluirá una lista de subclases conocidas (y para interfaces, clases de implementación conocidas).
fuente
Prueba ClassGraph . (Descargo de responsabilidad, yo soy el autor). ClassGraph admite la exploración de subclases de una clase determinada, ya sea en tiempo de ejecución o en tiempo de compilación, pero también mucho más. ClassGraph puede construir una representación abstracta de todo el gráfico de clase (todas las clases, anotaciones, métodos, parámetros de método y campos) en la memoria, para todas las clases en el classpath, o para las clases en paquetes incluidos en la lista blanca, y puede consultar ese gráfico de clase sin embargo usted quiere. ClassGraph admite más mecanismos de especificación de classpath y cargadores de clases que cualquier otro escáner, y también funciona a la perfección con el nuevo sistema de módulos JPMS, por lo que si basa su código en ClassGraph, su código será máximamente portátil. Vea la API aquí.
fuente
Lo hice hace varios años. La forma más confiable de hacerlo (es decir, con las API oficiales de Java y sin dependencias externas) es escribir un doclet personalizado para producir una lista que se pueda leer en tiempo de ejecución.
Puede ejecutarlo desde la línea de comando de esta manera:
o ejecutarlo desde una hormiga como esta:
Aquí está el código básico:
Para simplificar, he eliminado el análisis de argumentos de la línea de comandos y estoy escribiendo en System.out en lugar de un archivo.
fuente
Sé que llegué unos años tarde a esta fiesta, pero me encontré con esta pregunta tratando de resolver el mismo problema. Puede utilizar la búsqueda interna de Eclipse mediante programación, si está escribiendo un complemento de Eclipse (y, por lo tanto, aprovecha su almacenamiento en caché, etc.), para encontrar clases que implementen una interfaz. Aquí está mi primer corte (muy áspero):
El primer problema que veo hasta ahora es que solo estoy captando clases que implementan directamente la interfaz, no todas sus subclases, pero una pequeña recursión nunca perjudica a nadie.
fuente
Teniendo en cuenta las limitaciones mencionadas en las otras respuestas, también puede usar openpojo's
PojoClassFactory
( disponible en Maven ) de la siguiente manera:¿Dónde
packageRoot
está la cadena raíz de los paquetes en los que desea buscar (por ejemplo,"com.mycompany"
o incluso solo"com"
), ySuperclass
es su supertipo (esto también funciona en las interfaces)?fuente
Solo escribo una demostración simple para usar
org.reflections.Reflections
para obtener subclases de clase abstracta:https://github.com/xmeng1/ReflectionsDemo
fuente
Dependiendo de sus requisitos particulares, en algunos casos, el mecanismo del cargador de servicios de Java puede lograr lo que busca.
En resumen, permite a los desarrolladores declarar explícitamente que una clase subclasifica alguna otra clase (o implementa alguna interfaz) al incluirla en un archivo en el
META-INF/services
directorio del archivo JAR / WAR . Luego se puede descubrir usando lajava.util.ServiceLoader
clase que, cuando se le da unClass
objeto, generará instancias de todas las subclases declaradas de esa clase (o, siClass
representa una interfaz, todas las clases que implementan esa interfaz).La principal ventaja de este enfoque es que no es necesario escanear manualmente todo el classpath en busca de subclases: toda la lógica de descubrimiento está contenida dentro de la
ServiceLoader
clase, y solo carga las clases declaradas explícitamente en elMETA-INF/services
directorio (no todas las clases en el classpath) .Sin embargo, hay algunas desventajas:
META-INF/services
directorio. Esta es una carga adicional para el desarrollador y puede ser propensa a errores.ServiceLoader.iterator()
genera instancias de subclase, no susClass
objetos. Esto causa dos problemas:Aparentemente, Java 9 abordará algunas de estas deficiencias (en particular, las relacionadas con la creación de instancias de subclases).
Un ejemplo
Supongamos que está interesado en encontrar clases que implementen una interfaz
com.example.Example
:La clase
com.example.ExampleImpl
implementa esa interfaz:Declararía que la clase
ExampleImpl
es una implementaciónExample
al crear un archivo queMETA-INF/services/com.example.Example
contenga el textocom.example.ExampleImpl
.Luego, puede obtener una instancia de cada implementación de
Example
(incluida una instancia deExampleImpl
) de la siguiente manera:fuente
También debe tenerse en cuenta que, por supuesto, esto solo encontrará todas las subclases que existen en su classpath actual. Presumiblemente, esto está bien para lo que está viendo actualmente, y es probable que lo haya considerado, pero si en algún momento ha lanzado un
final
clase en la naturaleza (para diferentes niveles de "salvaje"), entonces es completamente factible que alguien más ha escrito su propia subclase que no conocerá.Por lo tanto, si desea ver todas las subclases porque desea hacer un cambio y verá cómo afecta el comportamiento de las subclases, tenga en cuenta las subclases que no puede ver. Idealmente todos sus métodos no privados, y la clase en sí misma debe estar bien documentada; realice cambios de acuerdo con esta documentación sin cambiar la semántica de los métodos / campos no privados y sus cambios deben ser compatibles con versiones anteriores, para cualquier subclase que siga su definición de la superclase al menos.
fuente
La razón por la que ve una diferencia entre su implementación y Eclipse es porque escanea cada vez, mientras que Eclipse (y otras herramientas) escanea solo una vez (durante la carga del proyecto la mayoría de las veces) y crea un índice. La próxima vez que solicite los datos, no volverá a escanear, pero mire el índice.
fuente
Estoy usando una biblioteca de reflexión, que escanea su classpath para todas las subclases: https://github.com/ronmamo/reflections
Así es como se haría:
fuente
Agréguelos a un mapa estático dentro (this.getClass (). GetName ()) el constructor de clases padre (o cree uno predeterminado) pero esto se actualizará en tiempo de ejecución. Si la inicialización diferida es una opción, puede probar este enfoque.
fuente
Puede usar la biblioteca org.reflections y luego crear un objeto de la clase Reflections. Con este objeto, puede obtener una lista de todas las subclases de una clase dada. https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html
fuente
Necesitaba hacer esto como un caso de prueba, para ver si se habían agregado nuevas clases al código. Esto es lo que hice
fuente