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

Я работаю над процессором аннотаций. Этот код компилируется:

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())

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

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


person Samuel Edwin Ward    schedule 15.01.2015    source источник
comment
По какой причине вы не можете использовать параметр Class вместо String?   -  person chrylis -cautiouslyoptimistic-    schedule 15.01.2015
comment
@chrylis SupportedAnnotationTypes принимает только String... в качестве входных данных.   -  person k_g    schedule 15.01.2015
comment
Извините, я умудрился пропустить, что это было из пространства имен APT. Я полагаю, вы могли бы определить свою собственную аннотацию, которая принимает Class и использовать процессор для генерации кода выше?   -  person chrylis -cautiouslyoptimistic-    schedule 15.01.2015
comment
Is there any way to coerce a class literal into its name at compile time? - нет, извините, но см. ответ капепа для хорошей альтернативы.   -  person gknicker    schedule 15.01.2015


Ответы (2)


Вместо использования аннотации ваш процессор может реализовать getSupportedAnnotationTypes() для предоставления имен поддерживаемых типов аннотаций во время выполнения:

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



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

Подпись вашей пользовательской аннотации должна использовать Class<?>[] вместо String[]:

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

Переопределите getSupportedAnnotationTypes и найдите пользовательскую аннотацию так же, как AbstractProcessor. Например вот так:

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

Вы можете определить свои собственные.

public @interface SupportedAnnotationTypes_Class {
    Class supported();
}

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

person k_g    schedule 15.01.2015