- Требовать, чтобы все ссылочные типы также были immutable, как String например. Для коллекций можно использовать Collections.unmodifiableCollection метод.
- Второй способ заключается в основе ООП: инкапсуляции. Ни один метод не возвращает объект по ссылке, только его копию, например вместо возврата коллекции можно вернуть её неизменяемую копию с помощью описанного выше метода.
Проверку сделал простой - просматриваются все не статические поля класса, они все должны быть private и final.
Стандартный javac нам уже не поможет. Будет использоваться утилита apt из стандартного набора JDK.
В начале напишем простенький класс и саму аннотацию:
- package me.drobushevich.blog.persist;
- import me.drobushevich.blog.annotation.Immutable;
- @Immutable
- public final class User {
- private int id;
- private final String name;
- public User(int id, String name) {
- this.id = id;
- this.name = name;
- }
- public int getId() {
- return id;
- }
- public String getName() {
- return name;
- }
- }
- package me.drobushevich.blog.annotation;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Retention(RetentionPolicy.RUNTIME)
- @Target( { ElementType.TYPE })
- public @interface Immutable {
- }
- package me.drobushevich.blog.annotation;
- import static com.sun.mirror.declaration.Modifier.FINAL;
- import static com.sun.mirror.declaration.Modifier.PRIVATE;
- import static com.sun.mirror.declaration.Modifier.STATIC;
- import java.util.Collection;
- import com.sun.mirror.apt.AnnotationProcessor;
- import com.sun.mirror.apt.AnnotationProcessorEnvironment;
- import com.sun.mirror.declaration.AnnotationMirror;
- import com.sun.mirror.declaration.AnnotationTypeDeclaration;
- import com.sun.mirror.declaration.ClassDeclaration;
- import com.sun.mirror.declaration.Declaration;
- import com.sun.mirror.declaration.FieldDeclaration;
- import com.sun.mirror.declaration.Modifier;
- public class ImmutableAnnotationProcessor implements AnnotationProcessor {
- public static final String IMMUTABLE_ANNOTATION_NAME = "me.drobushevich.blog.annotation.Immutable";
- private AnnotationProcessorEnvironment environment;
- private AnnotationTypeDeclaration immutableDeclaration;
- public ImmutableAnnotationProcessor(AnnotationProcessorEnvironment env) {
- environment = env;
- immutableDeclaration = (AnnotationTypeDeclaration) environment.getTypeDeclaration(IMMUTABLE_ANNOTATION_NAME);
- }
- @Override
- public void process() {
- Collection<Declaration> declarations = environment.getDeclarationsAnnotatedWith(immutableDeclaration);
- for (Declaration declaration : declarations) {
- processAnnotations(declaration);
- }
- }
- private void processAnnotations(Declaration declaration) {
- Collection<AnnotationMirror> annotations = declaration.getAnnotationMirrors();
- for (AnnotationMirror mirror : annotations) {
- if (mirror.getAnnotationType().getDeclaration().equals(immutableDeclaration)
- && declaration instanceof ClassDeclaration) {
- ClassDeclaration classDeclaration = (ClassDeclaration) declaration;
- System.out.println("Checking immutable class [" + classDeclaration.getQualifiedName() + "] ....");
- for (FieldDeclaration field : classDeclaration.getFields()) {
- Collection<Modifier> fieldModifiers = field.getModifiers();
- System.out.println("Field [" + field.getSimpleName() + "] modefiers: " + fieldModifiers);
- if (!fieldModifiers.contains(STATIC)
- && (!fieldModifiers.contains(PRIVATE) || !fieldModifiers.contains(FINAL))) {
- throw new RuntimeException("Field [" + field.getSimpleName()
- + "] of class [" + classDeclaration.getQualifiedName()
- + "] must be private and final, because this class is annotated as immutable");
- } else {
- System.out.println("Field [" + field.getSimpleName() + "] is ok");
- }
- }
- }
- }
- }
- }
- package me.drobushevich.blog.annotation;
- import static com.sun.mirror.apt.AnnotationProcessors.NO_OP;
- import static java.util.Collections.emptyList;
- import static java.util.Collections.singletonList;
- import static me.drobushevich.blog.annotation.ImmutableAnnotationProcessor.IMMUTABLE_ANNOTATION_NAME;
- import java.util.Collection;
- import java.util.Set;
- import com.sun.mirror.apt.AnnotationProcessor;
- import com.sun.mirror.apt.AnnotationProcessorEnvironment;
- import com.sun.mirror.apt.AnnotationProcessorFactory;
- import com.sun.mirror.declaration.AnnotationTypeDeclaration;
- public class ImmutableAnnotationProcessorFactory implements AnnotationProcessorFactory {
- @Override
- public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> declarations, AnnotationProcessorEnvironment env) {
- AnnotationProcessor result;
- if (declarations.isEmpty()) {
- result = NO_OP;
- } else {
- result = new ImmutableAnnotationProcessor(env);
- }
- return result;
- }
- @Override
- public Collection<String> supportedAnnotationTypes() {
- return singletonList(IMMUTABLE_ANNOTATION_NAME);
- }
- @Override
- public Collection<String> supportedOptions() {
- return emptyList();
- }
- }
- apt -classpath bin -factory me.drobushevich.blog.annotation.ImmutableAnnotationProcessorFactory \
- src/me/drobushevich/blog/persist/User.java
Комментариев нет:
Отправить комментарий