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