valepu
valepu

Reputation: 3315

Get all classes which extend class inferring certain generics

I am looking for some way to get all classes that extend a certain class and infer certain generics. I know that using Reflections i can get all classes that extend a class

final Reflections reflections = new Reflections("path.to.package");
Set<Class<? extends ClassBase>> classes = reflections.getSubTypesOf(ClassBase.class);

but i'm not sure how to get only those using certain generics. Here's an example of what i want to achieve

Let's assume i have ClassBase<T>

public class ClassBase<T> {}

Then i have ClassA ClassB ClassC, all of them extending ClassBase

public class ClassA extends ClassBase<Long> {}

public class ClassB extends ClassBase<Long> {}

public class ClassC extends ClassBase<Integer> {}

How do i get only ClassA and ClassB? I'm even ok with getting all the classes that extend ClassBase and then filter out those i don't need, as long as i end up with a set containing only those classes which extend ClassBase and infer <Long> as generic type.
Or better, i actually want a Set<Class<ClassBase<Long>>> not just a Set<Class<ClassBase>> which contains only those inferring Long as generic

Upvotes: 1

Views: 419

Answers (2)

NiVeR
NiVeR

Reputation: 9806

For people using spring and have those classes managed by the container (they are annotated with some spring annotation like @Service, @Component), you can get all the classes with the following approach:

@Inject Set <ClassBase <Long>> extensionsWithLongType; 

Upvotes: 0

Jorn Vernee
Jorn Vernee

Reputation: 33905

You can look at the inferred generic type using by using the function getGenericSuperclass and then getActualTypeArguments Here's an example:

// Given that all classes in this set are actually subclasses of ClassBase
Set<Class<? extends ClassBase>> classes = Set.of(ClassA.class, ClassB.class, ClassC.class);
System.out.println(classes); // [class test.ClassB, class test.ClassA, class test.ClassC]

@SuppressWarnings("unchecked")
Set<Class<? extends ClassBase<Long>>> result = classes.stream()
    .filter(cls -> {
        // Get superclass, which is ClassBase, so assume it's also parameterized
        ParameterizedType superClass = (ParameterizedType) cls.getGenericSuperclass();
        Type inferred = superClass.getActualTypeArguments()[0]; // get first type argument
        return inferred == Long.class; // check if it's 'Long'
    })
    // Can now say they all extend ClassBase<Long>
    .map(cls -> (Class<? extends ClassBase<Long>>) cls)
    .collect(Collectors.toSet());

System.out.println(result); // [class test.ClassA, class test.ClassB]

You get a Set<Class<? extends ClassBase<Long>>> which holds classes that extend ClassBase<Long>. A Set<Class<ClassBase<Long>>>, that you ask for, should only technically hold the class ClassBase itself, while you're actually interested in the sub classes, so you get Class<? extends ClassBase<Long>>.

Upvotes: 2

Related Questions