¿Cómo puedo enumerar todas las clases en un paquete y agregarlas a una lista?

Necesito enumerar todas las clases en un paquete y agregarlas a una lista. La versión no dinámica para una sola clase es la siguiente:

List allClasses = new ArrayList(); allClasses.add(String.class); 

¿Cómo puedo hacer esto de forma dinámica para agregar todas las clases en un paquete y todos sus subpaquetes?


Actualización: Después de leer las primeras respuestas, es absolutamente cierto que estoy tratando de resolver otro problema secundario, así que déjenme decirlo. Y sé que esto es posible ya que otras herramientas lo hacen. Ver nueva pregunta aquí .

Actualización: Leyendo esto de nuevo, puedo ver cómo se está malinterpretando. Estoy buscando enumerar todas las clases de MY PROJECT desde el sistema de archivos después de la comstackción.

**** ACTUALIZACIÓN 1 (2012) ****

OK, finalmente me he dedicado a limpiar el fragmento de código a continuación. Lo metí en su propio proyecto github e incluso agregué pruebas.

https://github.com/ddopson/java-class-enumerator

**** ACTUALIZACIÓN 2 (2016) ****

Para obtener un escáner de ruta de clase aún más robusto y rico en funciones, consulte https://github.com/lukehutch/fast-classpath-scanner/wiki . Recomiendo leer primero mi fragmento de código para obtener un alto nivel de comprensión y luego usar la herramienta de lukehutch para fines de producción.

**** Original Post (2010) ****

Estrictamente hablando, no es posible enumerar las clases en un paquete . Esto se debe a que un paquete no es más que un espacio de nombres (por ejemplo, com.epicapplications.foo.bar), y cualquier archivo jar en classpath podría agregar clases en un paquete. Lo que es peor, el cargador de clases cargará las clases bajo demanda, y parte de la ruta de clase podría estar en el otro lado de una conexión de red.

Es posible resolver un problema más restrictivo. por ejemplo, todas las clases en un archivo JAR, o todas las clases que un archivo JAR define dentro de un paquete en particular. Este es el escenario más común de todos modos.

Lamentablemente, no hay ningún código de marco para facilitar esta tarea. Debe escanear el sistema de archivos de manera similar a como el ClassLoader buscaría definiciones de clase.

Hay muchos ejemplos en la web de archivos de clase en directorios antiguos. La mayoría de nosotros en estos días trabajamos con archivos JAR.

Para que las cosas funcionen con archivos JAR, intenta esto …

 private static ArrayList> getClassesForPackage(Package pkg) { String pkgname = pkg.getName(); ArrayList> classes = new ArrayList>(); // Get a File object for the package File directory = null; String fullPath; String relPath = pkgname.replace('.', '/'); System.out.println("ClassDiscovery: Package: " + pkgname + " becomes Path:" + relPath); URL resource = ClassLoader.getSystemClassLoader().getResource(relPath); System.out.println("ClassDiscovery: Resource = " + resource); if (resource == null) { throw new RuntimeException("No resource for " + relPath); } fullPath = resource.getFile(); System.out.println("ClassDiscovery: FullPath = " + resource); try { directory = new File(resource.toURI()); } catch (URISyntaxException e) { throw new RuntimeException(pkgname + " (" + resource + ") does not appear to be a valid URL / URI. Strange, since we got it from the system...", e); } catch (IllegalArgumentException e) { directory = null; } System.out.println("ClassDiscovery: Directory = " + directory); if (directory != null && directory.exists()) { // Get the list of the files contained in the package String[] files = directory.list(); for (int i = 0; i < files.length; i++) { // we are only interested in .class files if (files[i].endsWith(".class")) { // removes the .class extension String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6); System.out.println("ClassDiscovery: className = " + className); try { classes.add(Class.forName(className)); } catch (ClassNotFoundException e) { throw new RuntimeException("ClassNotFoundException loading " + className); } } } } else { try { String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", ""); JarFile jarFile = new JarFile(jarPath); Enumeration entries = jarFile.entries(); while(entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); if(entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) { System.out.println("ClassDiscovery: JarEntry: " + entryName); String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", ""); System.out.println("ClassDiscovery: className = " + className); try { classes.add(Class.forName(className)); } catch (ClassNotFoundException e) { throw new RuntimeException("ClassNotFoundException loading " + className); } } } } catch (IOException e) { throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e); } } return classes; } 

Descubrí cómo hacer esto. Este es el procedimiento:

  1. Comience con una clase en el paquete raíz y obtenga la carpeta desde el cargador de clases
  2. Enumerar recursivamente todos los archivos .class en esta carpeta
  3. Convierta los nombres de los archivos a nombres de clase totalmente calificados
  4. Use Class.forName () para obtener las clases

Aquí hay algunos trucos desagradables que me ponen un poco incómodo, pero funciona, por ejemplo:

  1. Conversión de nombres de ruta a nombres de paquete utilizando manipulación de cadena
  2. Hard-coding el nombre del paquete raíz para permitir eliminar el prefijo de ruta

Lástima que stackoverflow no me permita aceptar mi propia respuesta …

Puedes probar mi biblioteca FastClasspathScanner .

Para enumerar todas las clases en un paquete, haga lo siguiente:

 Set classNames = new FastClassPathScanner("com.mypackage") .scan() .getNamesOfAllClasses(); 

Me temo que tendrá que escanear manualmente el classpath y los otros lugares donde java busca las clases (por ejemplo, el directorio ext o el classpath de arranque). Debido a que java usa la carga de clases perezosas, es posible que ni siquiera conozca las clases adicionales en sus paquetes que aún no se han cargado. También verifique la noción de paquetes “sellados”.

Es gracioso que esta pregunta surja de vez en cuando. El problema es que esta palabra clave se habría denominado más apropiadamente “espacio de nombres”. El paquete de Java no delinea un contenedor concreto que contiene todas las clases en el paquete en un momento dado. Simplemente define un token que las clases pueden usar para declarar que son miembros de ese paquete. Tendría que buscar a través de la ruta de clase completa (como indica otra respuesta) para determinar todas las clases en un paquete.

Esto es una advertencia: los contenedores ApplicationEngines / servlet como tomcat y JBoss tienen cargadores de clase jerárquicos . Obtener el cargador de clases del sistema no funcionará.

La forma en que funciona Tomcat (las cosas pueden haber cambiado, pero mi experiencia actual no me lleva a creer lo contrario) pero cada contexto de aplicación tiene su propio cargador de clases para que las clases para la aplicación ‘foo’ no colisionen con las clases para la aplicación ‘fooV2 ‘

Solo como un ejemplo. Si todas las clases se convirtieran en mundos en un contexto de clase uber, entonces no tendrías idea si usabas clases apropiadas para la versión 1 o la versión 2.

Además, cada uno necesita acceso a clases del sistema como java.lang.String. Esta es la jerarquía. Comprueba primero el contexto de la aplicación local y lo mueve hacia arriba (esta es mi situación actual, por cierto).

Para gestionar esto, un mejor enfoque sería: this.getClass (). GetClassloader ()

En mi caso, tengo un servicio web que necesita autodescubrirse en algunos módulos y obviamente residen en ‘este’ contexto del servicio web o en el contexto del sistema. Al hacer lo anterior, puedo verificar ambos. Con solo obtener el cargador de clases del sistema, no tengo acceso a ninguna de las clases de aplicaciones (y, por lo tanto, mis recursos son nulos).

Mira lo que está haciendo java.net.URLClassLoader. Nunca enumera clases, solo trata de encontrar clases cuando se le pide. Si desea enumerar las clases, necesitará obtener la ruta de clases, dividirla en directorios y archivos jar. Escanee los directorios (y sus subdirectorios) y los archivos jar para los archivos con el nombre * .class.

Puede valer la pena mirar proyectos de código abierto que parecen hacer la enumeración que desea (como Eclipse ) para inspirarse.

Si simplemente buscas cargar un grupo de clases relacionadas, Spring puede ayudarte.

Spring puede instanciar una lista o mapa de todas las clases que implementan una interfaz dada en una línea de código. La lista o mapa contendrá instancias de todas las clases que implementan esa interfaz.

Dicho esto, como alternativa a cargar la lista de clases fuera del sistema de archivos, en su lugar solo implemente la misma interfaz en todas las clases que desee cargar, independientemente del paquete. De esta forma, puede cargar (y crear instancias) todas las clases que desee independientemente del paquete en el que se encuentre.

Por otro lado, si tenerlos a todos en un paquete es lo que quieres, simplemente haz que todas las clases en ese paquete implementen una interfaz determinada.