在Java 8

时间:2015-08-19 07:38:03

标签: java reflection annotations java-8

我认为标题描述了这个问题。这是一些代码:

import static org.junit.Assert.assertEquals;
import java.lang.annotation.*;

public class Main {
    public static void main(String[] args) throws Exception {
        assertEquals("foo", Main.class.getDeclaredMethod("myMethod").getAnnotation(Anno.class).param());

        // the magic here -> set to bar

        assertEquals("bar", Main.class.getDeclaredMethod("myMethod").getAnnotation(Anno.class).param());
    }

    @Anno(param = "foo")
    public void myMethod() {}

    @Retention(RetentionPolicy.RUNTIME)
    @interface Anno {
        String param();
    }
}

到目前为止,我猜这是不可能的。似乎总是当你试图通过反射获得一个方法时,你只能获得副本,所有的值(如注释)都会从更深层的java层重新读取。在这些副本中,您可以更改值,但如果重新加载则这些值将消失。

我错过了什么或者它真的不可能吗?

2 个答案:

答案 0 :(得分:3)

注释是修饰符,就像privatesynchronized一样。它们是类的不变静态结构的一部分,并不打算进行修改。您可以入侵Reflection实现,使特定方法打印出您想要的内容,但除了黑客的肮脏之外,您只是没有更改注释,只是破解了特定库的数据结构。

还有其他反射或字节代码操作库,它们不使用内置的Reflection API,而是直接读取字节代码(例如通过getResource()或通过Instrumentation API)。这些库永远不会注意到你的操作。

进一步注意,由于这些值应该是嵌入在类文件中的常量,因此Reflection实现总是可以使用延迟提取加上根据无法控制的条件删除缓存的值,因为这些值总是可以被重新获得。也没有保证Reflection实现完全使用数据结构;它还可以生成返回常量值的代码。

换句话说,如果要将可变数据与方法关联,请不要使用注释。您可以简单地使用Map<Method,MutableData>,或者,如果您只有一个特定的方法,则声明一个好的旧static字段,它已经提供了您需要的所有功能,并且更容易处理。

答案 1 :(得分:1)

我发布了一个在Java 8之前执行相同任务的链接。似乎可以在Java 8(1.8.0_51)中执行相同的操作。整个测试设置

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

public class Test {
    public static void main(String... args) {
        Test t = new Test();
        t.check();
        Test.change();
        t.check();
        new Test().check();
    }

    public Test() {

    }

    public static void change() {
        try {
            Method m = Test.class.getMethod("myMethod");
            // The map has to be built for the first time
            m.getDeclaredAnnotations();
            Class<?> superclass = m.getClass().getSuperclass();
            Field declaredField = superclass.getDeclaredField("declaredAnnotations");
            declaredField.setAccessible(true);
            @SuppressWarnings("unchecked")
            Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) declaredField
                    .get(m);
            map.put(Anno.class, new Anno() {

                @Override
                public Class<? extends Annotation> annotationType() {
                    return Anno.class;
                }

                @Override
                public String param() {
                    return "new";
                }
            });
        } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | NoSuchFieldException
                | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public void check() {
        try {
            System.out.println(getClass().getMethod("myMethod").getAnnotation(Anno.class).param());
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }

    @Anno(param = "test")
    public void myMethod() {
    }

}

@Retention(RetentionPolicy.RUNTIME)
@interface Anno {
    String param();
}