2015-01-15 2 views
14

Я работаю над обработчиком аннотаций. Этот код компилируется:Могу ли я получить имя класса как константу времени компиляции без жесткого кодирования в строковом литерале?

package sand; 

import java.util.Set; 
import javax.annotation.processing.AbstractProcessor; 
import javax.annotation.processing.RoundEnvironment; 
import javax.annotation.processing.SupportedAnnotationTypes; 
import javax.lang.model.element.TypeElement; 

@SupportedAnnotationTypes("sand.Foo") 
public class FooProcessor extends AbstractProcessor { 

    @Override 
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 
     return false; // TODO 
    } 
} 

Однако я недоволен строковой константы «sand.Foo» (не слишком много в этом случае, но больше в общем на будущее).

Если Foo переименован или перенесен в другой пакет, этот код все равно будет компилироваться, но он не будет работать.

Я хотел бы сделать что-то вроде:

@SupportedAnnotationTypes(Foo.class) 

Таким образом, если изменения имени Foo, тем компиляция будет прервана и кто-то должен будет исправить файл.

Но это не работает, потому что Class не является String. Так что я пробовал:

@SupportedAnnotationTypes(Foo.class.getName()) 

Но компилятор не считает это постоянное выражение, которое требуется в данном контексте, так что не будет работать.

Есть ли способ принудить литерал класса к его имени во время компиляции?

+2

В чем причина того, что вы не можете использовать параметр 'Class' вместо' String'? – chrylis

+1

@chrylis SupportedAnnotationTypes принимает только строку «String ...». –

+3

Извините, мне удалось пропустить, что это было из пространства имен APT. Я полагаю, вы могли бы определить свою собственную аннотацию, которая принимает «класс» и использует процессор для генерации кода выше? – chrylis

ответ

5

Вместо того, чтобы использовать аннотацию, процессор может реализовать getSupportedAnnotationTypes() предоставить поддерживаемые имена типов аннотаций во время выполнения:

Set<String> getSupportedAnnotationTypes() { 
    Set<String> supportedAnnotationTypes = new HashSet<>(); 
    supportedAnnotationTypes.add(Foo.class.getName()); 
    return supportedAnnotationTypes; 
} 



В случае, если вы хотите продолжать использовать (не -стандартные) аннотации для этого, вы можете создать свою собственную аннотацию, которая принимает тип времени компиляции как аргумент, например, @k_g. @SupportedAnnotationTypes на самом деле ничего особенного, он используется только автоматически, когда вы расширяете AbstractProcessor. Взгляните на source code of AbstractProcessor.getSupportedAnnotationTypes().

Сигнатуры пользовательской аннотации следует использовать Class<?>[] вместо String[]:

@Target(TYPE) 
@Retention(RUNTIME) 
public @interface SupportedAnnotationTypes { 
    Class<?>[] value(); 
} 

Override getSupportedAnnotationTypes и искать свою собственную аннотацию точно так же, как AbstractProcessor. Например, например:

public Set<String> getSupportedAnnotationTypes() { 
    Class<?>[] types = getClass().getAnnotation(SupportedAnnotationTypes.class).value(); 
    return Arrays.stream(types).map(Class::getName).collect(Collectors.toSet()); 
} 
5

Вы можете указать свой собственный.

public @interface SupportedAnnotationTypes_Class { 
    Class supported(); 
} 

, а затем использовать @SupportedAnnotationTypes_Class(supported = sand.Foo.class), чтобы использовать его.