Почему значения строки аннотации не интернированы?
Следующий фрагмент печатает 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() {}
}
Ответы
Ответ 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);