Why are annotation string values not interned?
Asked Answered
D

2

18

The following snippet prints 4 distinct hash codes, despite reusing a string constant and literal. Why are string values not interned on annotation elements?

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() {}
}
Digestif answered 16/6, 2016 at 7:58 Comment(1)
Annotation string literals aren't part of the code and aren't subject to the same rules as string literals in the code, so there is really no reason why they should be pooled.Homocentric
A
10

String literal are interned but annotations are subject to parse and they are stored in byte arrays. If you look at the class java.lang.reflect.Method you can see this:

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

Take also a look at the method public Object getDefaultValue() of the same class to see how the AnnotationParser is called. The flow continues till here AnnotationParser.parseConst and enter in

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

The method ConstantPool.getUTF8At is a delegate to a native method. You can see the code here native implementation getUFT8At. The parsed constant is never interned and is never retrieved from the StringTable (where string are interned).

I think it could be a choice of implementation. Interning has been created to make more fast comparison between String literal and so is used only for interning literal usable in method implementation.

Apfel answered 17/6, 2016 at 12:38 Comment(0)
R
4

It's because you access to annotation at runtime and accordance with java spec - Example 3.10.5-1. String Literals, strings are newly created and therefore distinct.

All literal strings and compile-time string-valued constant expressions are automatically interned

In your case the value from test1 will be computed at runtime from native value() method (look at AnnotationDefault attribute).

String value() default CONSTANT;

Others cases also will be computed at runtime.

When you get a value from the annotation you have to explicitly to perform intern

String poolString = Foo.class.getMethod("test1").getAnnotation(Bar.class).value().intern();
System.out.println(poolString == Bar.CONSTANT); 
Rhinencephalon answered 7/7, 2016 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.