Programmieren mit Java IIhttp://sol.cs.hm.edu/4129Inhaltsverzeichnis
9. AnnotationenAufgaben
Aufgabe 2: Analyse der Codestruktur
Die Annotation Immutable soll eine Klasse als unveränderlich ausweisen. Diese Eigenschaft ist im Allgemeinen schwer nachzuweisen. In dieser Aufgabe geht es daher nur um eine vereinfachte Sicht von Unveränderlichkeit. Eine Klasse ist sicherlich unveränderlich, wenn
  1. alle Objektvariablen final und
  2. die Typen der Objektvariablen primitiv oder String sind.
Entwickeln Sie einen Prozessor, der diese Eigenschaften bei Immutable-annotierten Klassen überprüft und einen Fehler meldet, wenn sie nicht zutreffen.
Der Prozessor muss Variablendefinitionen untersuchen, die nicht einzeln mit Annotationen versehen sind und daher nicht direkt zur Verfügung stehen. Stattdessen dient ein Visitor-Objekt vom Typ ElementScanner7[25]
Die 7 im Namen zeigt an, dass in dieser ABC Methoden für Quelltextstrukturen von Java 7 definiert sind. Die entsprechende Klasse ElementScanner6 eignet sich für Quelltexte von Java-Version 6. In künftigen Java-Versionen wird es wahrscheinlich weiter abgeleitete Klassen mit neuen Methoden geben, die kommende Erweiterungen erfassen.
zum Besuchen der untergeordneten Programmelemente. Das folgende Programm zeigt eine minimale Implementierung eines entsprechenden Prozessors. Im Visitor gibt es Methoden, die beim Erreichen verschiedener Programmbausteine aufgerufen werden:[26]
Die Visitor-Methoden visitPackage und visitTypeParameter wurden der Kürze wegen weggelassen. Die Methode visitUnknown ist ein Fallback für künftige Java-Versionen, in denen weitere Elemente besucht werden. Die Implementierung von visitUnknown in ElementScanner7 wirft eine UnknownElementException.
visitVariable
Für Parameter, Objekt- und Klassenvariablen,
visitExecutable
für Methoden, einschließlich Konstruktoren und
visitType
für Interface- und Klassendefinitionen.
import java.util.*;

import javax.annotation.processing.*;

import javax.lang.model.*;

import javax.lang.model.element.*;

import javax.lang.model.util.*;



@SupportedAnnotationTypes("AnAnnotation")

@AnAnnotation

public class BasicVisitorProcessor extends AbstractProcessor {

@Override public boolean process(Set <? extends TypeElement > elements, RoundEnvironment env) {

ElementScanner7<Object, Object> scanner7 = new ElementScanner7<Object, Object>() {

@Override public Object visitVariable(VariableElement variable, final Object x) {

System.out.println("variable: " + variable);

return super.visitVariable(variable, x);

}



@Override public Object visitExecutable(ExecutableElement executable, Object x) {

System.out.println("executable: " + executable);

return super.visitExecutable(executable, x);

}



@Override public Object visitType(TypeElement type, Object x) {

System.out.println("type: " + type);

return super.visitType(type, x);

}

};

for(TypeElement typeElement: elements)

for(Element element: env.getElementsAnnotatedWith(typeElement))

scanner7.scan(element);

return true;

}

}

BasicVisitorProcessor.java: Rahmen eines Annotation-Prozessors mit Visitor.
Dieses Programm ist mit der trivialen Annotation AnAnnotation versehen, die es selbst verarbeitet. Man kann es auf sich selbst anwenden und erhält beim Übersetzen des eigenen Quelltexts die folgende Ausgabe:
$ javac -processor BasicVisitorProcessor BasicVisitorProcessor.java

type: BasicVisitorProcessor

executable: BasicVisitorProcessor()

executable: process(Set<? extends TypeElement>,RoundEnvironment)

variable: elements

variable: env
Ein ElementScanner7 kann den inneren Aufbau von Methodenrümpfen nicht sehen.
Bauen Sie den BasicVisitorProcessor zum ImmutableProcessor aus. Mit dem ImmutableProcessor soll der Compiler beispielsweise die annotierten Klassen Rational.java und BankAccount.java fehlerfrei übersetzen, weil alle Objektvariablen final und primitiv oder String sind. Stellen Sie sicher, dass der Annotationprozessor die Übersetzung einer Immutable-annotierten Klasse ohne final oder mit anderen Typen verweigert.