Найти все классы и интерфейсы, которые класс расширяет или реализует рекурсивно
Мне было интересно, существует ли простой способ определения полного списка типов, который класс Java расширяет или реализует рекурсивно?
например:
class Foo extends Bar implements I1, I2 {...}
class Bar implements I3 {...}
interface I1 extends I4, I5 {...}
interface I2 {...}
interface I3 {...}
interface I4 {...}
interface I5 {...}
class ClassUtil {
public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(Class<?> clazz){
???
}
}
import static org.junit.Assert.*;
public class ClassUtilTest {
@Test
public void shouldEqualClasses(){
Set<Class<?>> types = ClassUtil.getAllExtendedOrImplementedTypesRecursively(Foo.class);
Set<Class<?>> checklist = new HashSet<>();
checklist.add(Foo.class);
checklist.add(Bar.class);
checklist.add(I1.class);
checklist.add(I2.class);
checklist.add(I3.class);
checklist.add(I4.class);
checklist.add(I5.class);
assertTrue(checklist.containsAll(types));
assertTrue(types.containsAll(checklist));
}
}
Вспомните помощника по созданию Arquillian ShrinkWrap.
UPDATE: из-за объекта класса, не реализующего Comparable > Мне также нужно найти способ создания Set (или подобного класса) без реализации интерфейса Comparable (например, исключительно полагаясь на hashcode объекта класса).
UPDATE: изменил тест на использование hashset. Derp.
Ответы
Ответ 1
Следующая реализация метода выполняет то, что требует OP, оно перемещает иерархию наследования для каждого класса и интерфейса:
public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(Class<?> clazz) {
List<Class<?>> res = new ArrayList<>();
do {
res.add(clazz);
// First, add all the interfaces implemented by this class
Class<?>[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) {
res.addAll(Arrays.asList(interfaces));
for (Class<?> interfaze : interfaces) {
res.addAll(getAllExtendedOrImplementedTypesRecursively(interfaze));
}
}
// Add the super class
Class<?> superClass = clazz.getSuperclass();
// Interfaces does not have java,lang.Object as superclass, they have null, so break the cycle and return
if (superClass == null) {
break;
}
// Now inspect the superclass
clazz = superClass;
} while (!"java.lang.Object".equals(clazz.getCanonicalName()));
return new HashSet<Class<?>>(res);
}
Я тестировал с помощью JFrame.class
, и получил следующее:
Set<Class<?>> classes = getAllExtendedOrImplementedTypesRecursively(JFrame.class);
for (Class<?> clazz : classes) {
System.out.println(clazz.getName());
}
Вывод:
java.awt.Container
java.awt.Frame
javax.swing.JFrame
javax.swing.TransferHandler$HasGetTransferHandler
java.awt.Window
javax.accessibility.Accessible
javax.swing.RootPaneContainer
java.awt.Component
javax.swing.WindowConstants
java.io.Serializable
java.awt.MenuContainer
java.awt.image.ImageObserver
ОБНОВЛЕНИЕ: Для тестового примера OP он печатает:
test.I5
test.Bar
test.I2
test.I1
test.Foo
test.I3
test.I4
Ответ 2
В Apache Common Lang есть класс ClassUtils, в котором есть два метода, которые вы хотите..getAllSuperClasses() и .getAllInterfaces().
Ответ 3
Ключ, который вы хотите, находится в методе Class#getSuperclass()
:
public static Set<Class<?>> stuff(Class<?> target) {
Set<Class<?>> classesInterfaces = new HashSet<>();
classesInterfaces.add(target);
classesInterfaces.addAll(Arrays.asList(target.getInterfaces());
Class<?> superClass = target.getSuperclass();
if(superClass != null)
classesInterfaces.addAll(stuff(superClass));
}
Ответ 4
Из вопроса Как найти все подклассы данного класса в Java?, this answer может быть полезно:
Используя класс PojoClassImpl.java, вы можете получить суперкласс, вызвав метод getSuperClass()
. Я думаю, что вам достаточно написать рекурсивный метод.
Ответ 5
Если я правильно задал вопрос, вы хотите найти все суперклассы (класс и интерфейс) определенного класса. Если это так, вы можете проверить следующее решение
чтобы найти суперклассы
Class C = getClass();
while (C != null) {
System.out.println(C.getSimpleName());
C = C.getSuperclass();
}
чтобы найти интерфейсы
C = getClass();
for(Class adf: C.getInterfaces()){
System.out.println(adf.getSimpleName());
}
Ответ 6
Это очень просто, если ваш класс Foo, тогда ваш код будет таким,
public void getClassDetails() {
Class klass = Foo.class;
Class<?> superKlass = klass.getSuperClass();
Class[] interfaces = klass.getInterfaces();
}
Ответ 7
Я однажды применил аналогичный механизм, используя asm
на некоторой ветке ShrinkWrap https://github.com/mmatloka/shrinkwrap/commit/39d5c3aa63a9bb85e6d7b68782879ca10cca273b. Иногда проблема заключается в том, что класс может использовать объект, который представляет собой реализацию интерфейса, не упомянутую в другом файле, поэтому он все еще может выйти из строя во время развертывания.
Из того, что я знаю, некоторое время назад официальная позиция заключалась скорее не в том, чтобы включать такую функцию внутри ShrinkWrap
, а скорее полагаться на инструменты, например. JBoss Tools
, где должна быть функция, позволяющая добавлять рекурсивные классы.
Ответ 8
В Java8
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ClassUtil {
public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(final Class<?> clazz) {
return walk(clazz)
.filter(Predicate.isEqual(java.lang.Object.class).negate())
.collect(Collectors.toSet());
}
public static Stream<Class<?>> walk(final Class<?> c) {
return Stream.concat(Stream.of(c),
Stream.concat(
Optional.ofNullable(c.getSuperclass()).map(Stream::of).orElseGet(Stream::empty),
Arrays.stream(c.getInterfaces())
).flatMap(ClassUtil::walk));
}
}
Тестовый код:
import java.util.Set;
class Test {
public static void main(String[] args) {
final Set<Class<?>> set = ClassUtil.getAllExtendedOrImplementedTypesRecursively(Foo.class);
set.stream().map(Class::getName).forEach(System.out::println);
}
class Foo extends Bar implements I1, I2 {}
class Bar implements I3 {}
interface I1 extends I4, I5 {}
interface I2 {}
interface I3 {}
interface I4 {}
interface I5 {}
}
Вывод:
Test$Foo
Test$Bar
Test$I2
Test$I5
Test$I1
Test$I3
Test$I4