Подтвердить что ты не робот

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

Следующий фрагмент печатает 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() {}
}
4b9b3361

Ответ 1

Строковый литерал интернирован, но аннотации подвергаются анализу и хранятся в массивах байтов. Если вы посмотрите на класс 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 является делегатом метода native. Вы можете увидеть здесь исходную реализацию getUFT8At. Синтаксическая константа никогда не интернирована и никогда не извлекается из StringTable (где строка интернирована).

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

Ответ 2

Это потому, что вы получаете доступ к аннотации во время выполнения и в соответствии с java spec - пример 3.10.5-1. Строковые литералы, строки создаются и, следовательно, различны.

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

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

String value() default CONSTANT;

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

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

String poolString = Foo.class.getMethod("test1").getAnnotation(Bar.class).value().intern();
System.out.println(poolString == Bar.CONSTANT);