나는 이런 식으로하고 싶다 :
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을 확장하는 새 클래스를 간단하게 만들 수 있으며 자동으로 선택됩니다.
업데이트 : 2008 년 10 월 16 일 오전 9시 태평양 표준시 :
이 질문은 많은 훌륭한 반응을 가져 왔습니다. 감사합니다. 응답과 연구에서 필자가 실제로하고 싶은 것은 Java에서는 불가능하다는 것을 알았습니다. ddimitrov의 ServiceLoader 메커니즘과 같은 접근 방식이 작동하지만 원하는만큼 무겁기 때문에 문제를 Java 코드에서 외부 구성 파일로 옮길 수 있다고 생각합니다. 업데이트 5/10/19 (11 년 후!) @IvanNik의 답변 org.reflections 에 따라 이것을 도울 수있는 몇 가지 라이브러리가 있습니다 . @Luke Hutchison의 답변 에서 나온 ClassGraph 도 흥미로워 보입니다. 답변에는 몇 가지 가능성이 더 있습니다.
내가 원하는 것을 나타내는 또 다른 방법 : Animal 클래스의 정적 함수는 추가 구성 / 코딩없이 Animal에서 상속되는 모든 클래스를 찾아 인스턴스화합니다. 구성 해야하는 경우 어쨌든 Animal 클래스에서 인스턴스화 할 수도 있습니다. Java 프로그램은 .class 파일의 느슨한 페더레이션이기 때문에 그대로입니다.
흥미롭게도 C #에서는 이것이 사소한 것 같습니다 .
답변
org.reflections를 사용 합니다 .
Reflections reflections = new Reflections("com.mycompany");
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
다른 예시:
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Reflections reflections = new Reflections("java.util");
Set<Class<? extends List>> classes = reflections.getSubTypesOf(java.util.List.class);
for (Class<? extends List> aClass : classes) {
System.out.println(aClass.getName());
if(aClass == ArrayList.class) {
List list = aClass.newInstance();
list.add("test");
System.out.println(list.getClass().getName() + ": " + list.size());
}
}
}
답변
원하는 것을 수행하는 Java 방법은 ServiceLoader 메커니즘 을 사용하는 것 입니다.
또한 많은 사람들이 잘 알려진 클래스 경로 위치 (예 : /META-INF/services/myplugin.properties)에 파일을 둔 다음 ClassLoader.getResources () 를 사용하여 모든 jar에서이 이름을 가진 모든 파일을 열거 함으로써 자신의 롤 을 생성합니다. 이를 통해 각 jar는 자체 공급자를 내보낼 수 있으며 Class.forName ()을 사용하여 리플렉션하여 인스턴스화 할 수 있습니다.
답변
측면 지향적 인 관점에서 이것을 생각하십시오. 실제로하고 싶은 것은 런타임에 Animal 클래스를 확장 한 모든 클래스를 아는 것입니다. (제목보다 문제에 대해 약간 더 정확한 설명이라고 생각합니다. 그렇지 않으면 런타임 질문이 없다고 생각합니다.)
그래서 당신이 생각하는 것은 인스턴스화되고있는 현재 클래스의 유형을 정적 배열에 추가하는 기본 클래스 (Animal) 생성자를 만드는 것입니다.
대략적으로;
public abstract class Animal
{
private static ArrayList<Class> instantiatedDerivedTypes;
public Animal() {
Class derivedClass = this.getClass();
if (!instantiatedDerivedClass.contains(derivedClass)) {
instantiatedDerivedClass.Add(derivedClass);
}
}
물론, instantiatedDerivedClass를 초기화하려면 Animal에 정적 생성자가 필요합니다. 아마도 이것이 원하는 것을 할 것이라고 생각합니다. 이것은 실행 경로에 따라 다릅니다. 호출되지 않은 Animal에서 파생 된 Dog 클래스가 있으면 Animal 클래스 목록에 없습니다.
답변
불행히도 이것은 ClassLoader가 사용 가능한 클래스를 알려주지 않으므로 완전히 가능하지는 않습니다. 그러나 다음과 같은 일을 상당히 가깝게 할 수 있습니다.
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
}
}
}
}
편집 : johnstok은 독립형 Java 응용 프로그램에서만 작동하며 응용 프로그램 서버에서는 작동하지 않는다는 의견이 정확합니다.
답변
기존 코드를 리팩토링하지 않고 간단하고 빠른 것이 필요한 경우 Stripes Framework 에서 ResolverUtil ( raw source )을 사용할 수 있습니다 .
다음은 클래스를로드하지 않은 간단한 예입니다.
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 {}
응용 프로그램 서버에서도 작동합니다.
코드는 기본적으로 다음을 수행합니다.
- 지정한 패키지의 모든 리소스를 반복합니다.
- .class로 끝나는 리소스 만 유지
- 다음을 사용하여 해당 클래스를로드하십시오.
ClassLoader#loadClass(String fullyQualifiedName)
- 여부를 확인
Animal.class.isAssignableFrom(loadedClass);
답변
주어진 클래스의 모든 서브 클래스를 나열하는 가장 강력한 메커니즘은 현재 ClassGraph 입니다. 새로운 JPMS 모듈 시스템을 포함하여 가장 광범위한 클래스 경로 지정 메커니즘을 처리하기 때문 입니다. (저는 저자입니다.)
List<Class<Animal>> animals;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("com.zoo.animals")
.enableClassInfo().scan()) {
animals = scanResult
.getSubclasses(Animal.class.getName())
.loadClasses(Animal.class);
}
답변
Java는 동적으로 클래스를로드하므로 클래스의 유니버스는 이미로드되었지만 아직 언로드되지 않은 클래스입니다. 아마도로드 된 각 클래스의 수퍼 타입을 확인할 수있는 사용자 정의 클래스 로더로 무언가를 수행 할 수 있습니다. 로드 된 클래스 세트를 쿼리하는 API가 없다고 생각합니다.