I have a function that provide a map of classes annotated with a given annotation
void <A extends Annotation> ImmutableMap<Class<?>, A> find(Class<A> annotation, String packageBase) {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
return ClassPath.from(loader).getTopLevelClassesRecursive(packageBase).stream()
.filter(x -> x.load().getAnnotation(annotation) != null)
.collect(Collectors.collectingAndThen(Collectors
.toMap(ClassPath.ClassInfo::load, x -> x.load().getAnnotation(annotation)), ImmutableMap::copyOf));
}
I would like to make a general provider with a cache, like the following example
@Singleton
public class AnnotatedClassProvider {
private final Map<Class<? extends Annotation>, ImmutableMap<Class<?>, Object>> cache;
private final String basePackage;
public AnnotatedClassProvider(String basePackage) {
this.basePackage = basePackage;
this.cache = Maps.newHashMap();
}
public <A extends Annotation> ImmutableMap<Class<?>, A> get(Class<A> annotation) {
ImmutableMap<Class<?>, A> cached = cache.get(annotation);
if (cached != null)
return cached;
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
cached = ClassPath.from(loader).getTopLevelClassesRecursive(basePackage).stream()
.filter(x -> x.load().getAnnotation(annotation) != null)
.collect(Collectors.collectingAndThen(Collectors
.toMap(ClassPath.ClassInfo::load, x -> x.load().getAnnotation(annotation)), ImmutableMap::copyOf));
this.cache.put(annotation, cached);
return (cached);
}
}
My problem: I don't find the way to replace Object
by a generic A
like in the get
function, for the following map:
private final Map<Class<? extends Annotation>, ImmutableMap<Class<?>, Object>> cache;
EDIT:
It compiles when I don't specify the map generics but I have to cast in the get method. Is there a way to avoid this?
private final Map<Class<? extends Annotation>, ImmutableMap> cache;
I think it should look like this
private final <A extends Annotation> Map<Class<A>, ImmutableMap<Class<?>, A>> cache;
It seems to be impossible without a cast, so the following lines fixed my problem:
private final Map<Class<? extends Annotation>, ImmutableMap> cache;
public <A extends Annotation> ImmutableMap<Class<?>, A> get(Class<A> annotation) {
@SuppressWarnings("unchecked")
ImmutableMap<Class<?>, A> cached = cache.get(annotation);
...
}
For interesting people, that's how my provider look now
Interface
public interface AnnotatedClassProvider {
<A extends Annotation> ImmutableMap<Class<?>, A> get(Class<A> annotation) throws IOException;
}
Abstract class
public abstract class AbstractAnnotatedClassProvider implements AnnotatedClassProvider {
private final Map<Class<? extends Annotation>, ImmutableMap> cache;
public AbstractAnnotatedClassProvider() {
this.cache = Maps.newHashMap();
}
protected final <A extends Annotation> ImmutableMap<Class<?>, A> find(Class<A> annotation, @Nullable String basePackage) throws IOException {
@SuppressWarnings("unchecked")
ImmutableMap<Class<?>, A> cached = cache.get(annotation);
if (cached != null)
return cached;
ClassPath classPath = ClassPath.from(Thread.currentThread().getContextClassLoader());
ImmutableSet<ClassPath.ClassInfo> set = basePackage == null
? classPath.getAllClasses()
: classPath.getTopLevelClasses(basePackage);
cached = set.stream()
.filter(x -> x.load().getAnnotation(annotation) != null)
.collect(Collectors.collectingAndThen(Collectors
.toMap(ClassPath.ClassInfo::load, x -> x.load().getAnnotation(annotation)), ImmutableMap::copyOf));
this.cache.put(annotation, cached);
return (cached);
}
}
Implementations
public final class Providers {
public static AnnotatedClassProvider newBased(String basePackage) {
return new AbstractAnnotatedClassProvider() {
@Override
public <A extends Annotation> ImmutableMap<Class<?>, A> get(Class<A> annotation) throws IOException {
return super.find(annotation, basePackage);
}
};
}
public static AnnotatedClassProvider newSimple() {
return new AbstractAnnotatedClassProvider() {
@Override
public <A extends Annotation> ImmutableMap<Class<?>, A> get(Class<A> annotation) throws IOException {
return super.find(annotation, null);
}
};
}
}
Example
@Retention(RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
String value();
}
package com.test
@Controller("mainController")
public class Main {
public static void main(String[] args) {
AnnotatedClassProvider provider = Providers.newBased("com.test");
Map<Class<?>, Controller> classes = provider.get(Controller.class);
classes.forEach((x, y ) -> System.out.println(String.format("Class: %s annotated with %s with value %s",
x.getName(), y.getClass().getName(), y.value())));
}
}
Output: Class: Main.java annotated with Controller.class with value mainController
Thanks for all comments.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.