tipos poo polimorfismo hijo herencia ejemplos derivada clase java reflection inheritance

poo - En tiempo de ejecución, encuentre todas las clases en una aplicación Java que extienda una clase base



super en java (12)

Este es un problema difícil y necesitará encontrar esta información usando análisis estático, no está disponible fácilmente en tiempo de ejecución. Básicamente, obtenga la ruta de clases de su aplicación y escanee las clases disponibles y lea la información del código de bytes de una clase de la que hereda. Tenga en cuenta que una clase Dog puede no heredar directamente de Animal pero puede heredar de Pet, que a su vez hereda de Animal, por lo que deberá realizar un seguimiento de esa jerarquía.

Quiero hacer algo como esto:

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() );

Entonces, quiero ver todas las clases en el universo de mi aplicación, y cuando encuentro una que desciende de Animal, quiero crear un nuevo objeto de ese tipo y agregarlo a la lista. Esto me permite agregar funcionalidad sin tener que actualizar una lista de cosas. Puedo evitar lo siguiente:

List<Animal> animals = new ArrayList<Animal>(); animals.add( new Dog() ); animals.add( new Cat() ); animals.add( new Donkey() ); ...

Con el enfoque anterior, simplemente puedo crear una nueva clase que amplíe Animal y se seleccionará automáticamente.

ACTUALIZACIÓN: 16/10/2008 9:00 a.m. Hora estándar del Pacífico:

Esta pregunta ha generado muchas respuestas excelentes, gracias. A partir de las respuestas y mi investigación, descubrí que lo que realmente quiero hacer simplemente no es posible en Java. Existen enfoques, como el mecanismo ServiceLoader de ddimitrov, que pueden funcionar, pero son muy pesados ​​para lo que yo quiero, y creo que simplemente transfiero el problema del código de Java a un archivo de configuración externo.

Otra forma de expresar lo que quiero: una función estática en mi clase Animal encuentra e instancia todas las clases que heredan de Animal, sin ninguna configuración / codificación adicional. Si tengo que configurar, también podría instanciarlos en la clase Animal de todos modos. Entiendo que debido a que un programa Java es solo una federación suelta de archivos .class, esa es la forma en que es.

Curiosamente, parece que esto es bastante trivial en C #.


Gracias a todos los que respondieron esta pregunta.

Parece que esto es realmente un hueso duro de roer. Terminé rindiéndome y creando una matriz estática y getter en mi clase base.

public abstract class Animal{ private static Animal[] animals= null; public static Animal[] getAnimals(){ if (animals==null){ animals = new Animal[]{ new Dog(), new Cat(), new Lion() }; } return animals; } }

Parece que Java simplemente no está configurado para la auto-descubrimiento de la manera en que C # es. Supongo que el problema es que, dado que una aplicación Java es simplemente una colección de archivos .class en un directorio / archivo jar en alguna parte, el tiempo de ejecución no conoce una clase hasta que se hace referencia a ella. En ese momento el cargador lo carga, lo que intento hacer es descubrirlo antes de hacer referencia a él, lo que no es posible sin tener que salir al sistema de archivos y mirar.

Siempre me gusta el código que puede descubrirse a sí mismo en lugar de tener que contarlo, pero esto también funciona.

¡Gracias de nuevo!


Java carga dinámicamente las clases, por lo que su universo de clases sería solo aquellas que ya se han cargado (y aún no se han descargado). Quizás pueda hacer algo con un cargador de clases personalizado que pueda verificar los supertipos de cada clase cargada. No creo que haya una API para consultar el conjunto de clases cargadas.


La forma Java de hacer lo que desea es usar el mecanismo ServiceLoader .

También muchas personas obtienen su propio archivo al tener un archivo en una ubicación de classpath bien conocida (es decir, /META-INF/services/myplugin.properties) y luego usar ClassLoader.getResources() para enumerar todos los archivos con este nombre de todos los archivos jar. Esto permite que cada contenedor exporte sus propios proveedores y usted puede instanciarlos por reflexión usando Class.forName ()


Lamentablemente, esto no es del todo posible ya que ClassLoader no le dirá qué clases están disponibles. Sin embargo, puede acercarse bastante haciendo algo como esto:

for (String classpathEntry : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) { if (classpathEntry.endsWith(".jar")) { File jar = new File(classpathEntry); JarInputStream is = new JarInputStream(new FileInputStream(jar)); JarEntry entry; while( (entry = is.getNextJarEntry()) != null) { if(entry.getName().endsWith(".class")) { // Class.forName(entry.getName()) and check // for implementation of the interface } } } }

Editar: johnstok es correcto (en los comentarios) que esto solo funciona para aplicaciones Java independientes, y no funcionará en un servidor de aplicaciones.


Piense en esto desde un punto de vista orientado al aspecto; lo que realmente quieres hacer es conocer todas las clases en tiempo de ejecución que HAN extendido la clase Animal. (Creo que es una descripción de tu problema un poco más precisa que tu título, de lo contrario, no creo que tengas una pregunta en tiempo de ejecución).

Entonces, lo que creo que quiere es crear un constructor de su clase base (Animal) que agregue a su matriz estática (prefiero ArrayLists, yo mismo, pero a cada uno de ellos) el tipo de la clase actual que se está instanciando.

Entonces, aproximadamente;

public abstract class Animal { private static ArrayList<Class> instantiatedDerivedTypes; public Animal() { Class derivedClass = this.getClass(); if (!instantiatedDerivedClass.contains(derivedClass)) { instantiatedDerivedClass.Add(derivedClass); } }

Por supuesto, necesitarás un constructor estático en Animal para inicializar la instanciaDirivedClass ... Creo que esto hará lo que probablemente quieras. Tenga en cuenta que esto depende de la ruta de ejecución; si tiene una clase de Perro que deriva de Animal que nunca se invoca, no la tendrá en su lista de Animal Class.


Podría usar ResolverUtil ( fuente sin procesar ) desde el Framework Stripes
si necesita algo simple y rápido sin refactorizar ningún código existente.

Aquí hay un ejemplo simple que no ha cargado ninguna de las clases:

package test; import java.util.Set; import net.sourceforge.stripes.util.ResolverUtil; public class BaseClassTest { public static void main(String[] args) throws Exception { ResolverUtil<Animal> resolver = new ResolverUtil<Animal>(); resolver.findImplementations(Animal.class, "test"); Set<Class<? extends Animal>> classes = resolver.getClasses(); for (Class<? extends Animal> clazz : classes) { System.out.println(clazz); } } } class Animal {} class Dog extends Animal {} class Cat extends Animal {} class Donkey extends Animal {}

Esto también funciona en un servidor de aplicaciones ya que fue diseñado para funcionar;

El código básicamente hace lo siguiente:

  • iterar sobre todos los recursos en los paquetes que especifique
  • mantener solo los recursos que terminan en .class
  • Cargue esas clases usando ClassLoader#loadClass(String fullyQualifiedName)
  • Comprueba si Animal.class.isAssignableFrom(loadedClass);

Resolví este problema de forma muy elegante utilizando anotaciones a nivel de paquete y luego, hacer esa anotación tiene como argumento una lista de clases.

Encuentra clases de Java que implementan una interfaz

Las implementaciones solo tienen que crear un paquete-info.java y colocar la anotación mágica con la lista de clases que desean admitir.


Una forma es hacer que las clases usen un inicializador estático ... No creo que estos sean heredados (no funcionará si lo son):

public class Dog extends Animal{ static { Animal a = new Dog(); //add a to the List }

Requiere que agregue este código a todas las clases involucradas. Pero evita tener un gran bucle feo en alguna parte, probando cada clase buscando niños de Animal.


Usando OpenPojo puedes hacer lo siguiente:

String package = "com.mycompany"; List<Animal> animals = new ArrayList<Animal>(); for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(package, Animal.class, null) { animals.add((Animal) InstanceFactory.getInstance(pojoClass)); }


Yo uso org.reflections :

Reflections reflections = new Reflections("com.mycompany"); Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);


utilizar esta

public static Set<Class> getExtendedClasses(Class superClass) { try { ResolverUtil resolver = new ResolverUtil(); resolver.findImplementations(superClass, superClass.getPackage().getName()); return resolver.getClasses(); } catch(Exception e) {Log.d("Log:", " Err: getExtendedClasses() ");} return null; } getExtendedClasses(Animals.class);

Editar:

  • biblioteca para (ResolverUtil): Stripes