Почему значения строк аннотации не интернированы?

Следующий фрагмент выводит 4 различных хеш-кода, несмотря на многократное использование строковой константы и литерала. Почему строковые значения не интернированы в элементах аннотации?

public class Foo {
    @Retention(RetentionPolicy.RUNTIME)
    @interface Bar {
        String CONSTANT = "foo";

        String value() default CONSTANT;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(System.identityHashCode(Bar.CONSTANT));
        System.out.println(System.identityHashCode(Foo.class.getMethod("test1").getAnnotation(Bar.class).value()));
        System.out.println(System.identityHashCode(Foo.class.getMethod("test2").getAnnotation(Bar.class).value()));
        System.out.println(System.identityHashCode(Foo.class.getMethod("test3").getAnnotation(Bar.class).value()));
    }

    @Bar
    public void test1() {}

    @Bar("foo")
    public void test2() {}

    @Bar(Bar.CONSTANT)
    public void test3() {}
}

2 ответа

Решение

Строковый литерал интернирован, но аннотации подвергаются анализу и хранятся в байтовых массивах. Если вы посмотрите на класс java.lang.reflect.Method Вы можете увидеть это:

private byte[]              annotations;
private byte[]              parameterAnnotations;
private byte[]              annotationDefault;  

Взгляните также на метод public Object getDefaultValue() того же класса, чтобы увидеть, как вызывается AnnotationParser. Поток продолжается до здесь AnnotationParser.parseConst и введите в

case 's':
  return constPool.getUTF8At(constIndex);

Метод ConstantPool.getUTF8At является делегатом нативного метода. Вы можете увидеть здесь код нативной реализации getUFT8At. Анализируемая константа никогда не интернируется и никогда не извлекается из StringTable (где интернируется строка).

Я думаю, что это может быть выбор реализации. Интернирование было создано для более быстрого сравнения литерала String и поэтому используется только для интернирования литерала, используемого в реализации метода.

Это потому, что вы получаете доступ к аннотации во время выполнения и в соответствии со спецификацией Java - Пример 3.10.5-1. Строковые литералы, строки создаются заново и поэтому различаются.

Все литеральные строки и строковые константные выражения во время компиляции автоматически интернируются

В вашем случае значение от test1 будет вычисляться во время выполнения из собственного метода value() (смотрите атрибут AnnotationDefault).

String value() default CONSTANT;

Другие случаи также будут вычислены во время выполнения.

Когда вы получаете значение из аннотации, вы должны явно выполнить интерна

String poolString = Foo.class.getMethod("test1").getAnnotation(Bar.class).value().intern();
System.out.println(poolString == Bar.CONSTANT); 
Другие вопросы по тегам