Why doesn't reflection work when a class retur

2019-02-25 04:35发布

问题:

I have encountered a somewhat strange behaviour. I use annotations to mark certain classes with a particular purpose, and then I use org.reflections library to find all the classes with the particular annotation. However when a class implements a method that returns a lambda function, the reflection won't find the annotated class anymore. The signature of the class is unchanged.

Example of annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
   String someValue();
}

Using the annotation (EDIT - added the BaseClass):

public class BaseClass {

    public Consumer<Integer> doSomething(){ return null;}
}


@MyAnnotation(someValue = "a")
public class FooBar extends BaseClass {

    @Override
    public Consumer<Integer> doSomething() {
        return null;
    }
}



@MyAnnotation(someValue = "bazbar")
public class BarClass extends BaseClass {

    @Override
    public  Consumer<Integer> doSomething(){
        return (x) -> {};
    }
}

Finding the annotations

private static final Reflections reflections = new Reflections("com.mypackage");

Set<Class<?>> clazzes  = reflections.getTypesAnnotatedWith(MyAnnotation.class);

Now - the set 'clazzes' only contains 1 single class, the FooBar class. If I remove the lambda return value from the BazBar class that one too shows up, but as soon as one method in the class returns a lambda the reflection wont work.

I'm lost, any ideas?

回答1:

From the issues list it seems that this library isn't fit for Java 8 features yet. I think your best bet is reporting this problem and maybe provide a fix.



回答2:

You may perform the lookup without any third-party code as follows:

private static <T extends Annotation> Set<Class<?>> getAnnotated(
        Class<?> context, Class<T> anno) throws IOException, URISyntaxException {

    URI clURI = context.getResource(context.getSimpleName()+".class").toURI();
    if(!clURI.getScheme().equals("file")) try {
        FileSystems.getFileSystem(clURI);
    } catch(FileSystemNotFoundException ex) {
        FileSystems.newFileSystem(clURI, Collections.emptyMap());
    }
    String pkg=context.getPackage().getName();
    return Files.list(Paths.get(clURI).getParent())
        .map(p->p.getFileName().toString())
        .filter(s->s.endsWith(".class"))
        .map(s->s.substring(0, s.length()-6))
        .map(s-> { try {
            return context.getClassLoader().loadClass(pkg+'.'+s);
          } catch(ClassNotFoundException ex) { return null; } })
        .filter(c -> c!=null && c.isAnnotationPresent(anno))
        .collect(Collectors.toSet());
}

The advantage of Path over good old File is that it abstracts over the filesystem (storage) and thus can be used to list package members even if the Class is stored in a jar file (or any other kind of class storage for which a FileSystemProvider exists).

You may use it as

Set<Class<?>> clazzes  = getAnnotated(context, MyAnnotation.class);

where context is a Class within the package for which you want to get a annotated classes.