Statische Methode "vererben" - Zwang durch annotation processor

adalbert

Mitglied
Hallo zusammen!

Ich möchte für eine Anwendung ein einfaches Plugin-System realisieren. Die Applikation durchsucht nach dem Start ein definiertes Unterverzeichniss nach *.jar Dateien, öffnet diese und sucht via Reflection nach allen nicht abstrakten Klassen die von der abstrakten Klasse PluginFactory erben. Von diesen Klassen erwarte ich eine parameterlose statische öffentliche Methode createFactory, die mir eine Factory für dieses jeweilige Plugin liefert.
Soweit so gut, das funktioniert bisher einwandfrei. Mich stört an dieser Lösung nur, dass es (scheinbar) keine Möglichkeit gibt die statische Methode zu erzwingen, so dass es zu einem Compilerfehler o.ä. kommt wenn eine entsprechende Klasse nicht eine eigene Version der factory-Methode implementiert. Einfach gesagt
[JAVA=1337]
public abstract class PluginFactory {
public abstract static PluginFactory createFactory() ;
}
[/code]
wäre genau das was ich gerne hätte. Leider steht das ja etwas im wiederspruch mit OO-Denke und dem was Java bietet.

Meine Idee war nun eine annotation zu erstellen, die von einem eigenen annotation processor ausgewertet wird. Der annotation gebe ich den Namen der statischen Factory Methode mit. Annotiert durch @Inherits wird die Annotation ja auch an die ableitungen weiter vererbt.
Der annotation processor muss nun alle gekennzeichneten Klassen durchlaufen und prüfen ob diese eine statische öffentliche Methode des gesetzten Namens imlementiert.

Hier die annotation:
Java:
package test;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Inherited
@Documented
@Retention( RetentionPolicy.RUNTIME)
@Target( ElementType.TYPE )
public @interface StaticInherit {
	public String value();
}

und hier das wichtigste aus dem annotation Processor

Java:
@Override
	public boolean process(Set<? extends TypeElement> annotations,
			RoundEnvironment roundEnv) {

//		for (Element element : roundEnv.getRootElements()) {
//			checkClass(element);
//			
//		}
		
		for (TypeElement type : annotations) {
			for (Element element : roundEnv.getElementsAnnotatedWith(type)) {
				checkClass(element);
			}
		}

		return true;
	}
	
	private void checkClass(Element element) {
		boolean found = false;
		this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "found occurence: " + element.toString(), element);
		
		StaticInherit inherit = element.getAnnotation(StaticInherit.class);
		String name = inherit.value();
		
		if (name == null || "".equals(name))
			this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "no value: " + element.toString(), element);
		else
			this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "value: " + name + " " + element.toString(), element);

		for (ExecutableElement execelement : ElementFilter.methodsIn(element.getEnclosedElements())) {
			if (execelement.getSimpleName().toString().equals(name)) {
				if (execelement.getModifiers().contains(Modifier.PUBLIC)
						&& execelement.getModifiers().contains(Modifier.STATIC)) {
					found = true;
					this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "method accepted: " + execelement.toString() + " " 
							+ System.currentTimeMillis(), execelement);
					break;
				}
			}
		}

		if (!found) {
			this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "public static method called " + name + "() not implemented!", element);
		} else {
			this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "check not performed!", element);
		}
	}

Der Code oben ist etwas abgewandelt um den Fehler leichter zu finden, also die prüfung auf abstract in der class und den Rückgabetyp der Methode habe ich mal aussen vorgelassen.

Der Code wirft ja viele Warnings, welche mir eclipse auch schön brav auswirft.

Auf folgende Beispielklassen wird das ganze nun angewandt
Java:
package test.banane;

import test.StaticInherit;

@StaticInherit("createFactory")
public abstract class SuperClass {
	public static void createFactory() {
		
	}
}
Java:
package test.banane;

public class SubClass extends SuperClass{

}

Die Warnings die der Compiler wirft sind im anhang zu finden.
Die erste Warnung kann ignoriert werden, da der Check für die Classe SuperClass ja ausgeführt wurde.
Die beiden nächsten sind interessanter: found occurence für SuperClass und SubClass. Die vererbung der annotation hat also geklappt.
Die 4te Warnung method accepted ist auch sehr schön, da die gesuchte Methode in der Klasse SuperClass gefunden wurde.
Die 5te Warnung gibt den Namen der gesuchten methode an, der passt auch.

Allerdings wird scheinbar in der Klasse SubClass keinerlei Prüfung durchgeführt, obwohl die Klasse vom Annotation processor gefunden wurde.


Ich hab jetzt etliche Stunden google nach diesem Thema gelöchert aber nichts brauchbares gefunden. Hat von euch jemand eine Idee was falsch sein könnte, oder wie mein Problem gelöst werden könnte?

(Und bitte keine Belehrungen darüber, dass es mehr als einen Grund gibt, warum statische methoden nicht vererbbar sind. Sowohl die Tatsache als auch die Gründe sind mir hinreichend bekannt. :) )


Gruß
Adalbert
 

Landei

Top Contributor
Wo ist der Unterschied zwischen createFactory() und einem parameterlosen Konstruktor? Angenommen, du hast folgendes:

Java:
public interface PluginFactory {
    //entsprechende Methoden
}

public MyPluginFactory implements PluginFactory {
   //Methoden-Implementierungen
}

Dann kannst du per Reflection MyPluginFactory finden, indem du nach dem Interface PluginFactory suchst, und mit Class.forName("MyPluginFactory").newInstance eine Factory erzeugen. Leider kann in Java ohne Reflection die Existenz eines öffentlichen argumentlosen Konstruktors nicht garantiert werden. Es gibt Lösungen, aber die sind nicht hübsch, z.B. [JavaSpecialists 167] - Annotation Processing Tool
 

Noctarius

Top Contributor
Ich würde für die Klassensuche Tools wie Javassist oder ähnliches nutzen um die Klassen (zur Prüfung nach dem Interface) nicht echt in den Classloader laden zu müssen.

Alternative ist einen neuen Classloader instanzieren, Klassen mit diesen Classloader laden, Interface prüfen, CanonicalPath der Klasse speichern, am Ende den neuen Classloader wegwerfen und alle eventuellen Referenzen auf die geholten Klassen löschen.

Dann mit der Liste von CanonicalPaths und einem sauberen Classloader die Klassen erneut laden und nutzen.
 

adalbert

Mitglied
Hallo Landei!

Der unterschied ist natürlich in diesem Falle minimal, beide Lösungen sind gleichwertig.
Das Problem: Die existenz des Parameterlosen Konstruktors bzw. der statischen Methode wird via Reflection erst zur Laufzeit geprüft. Ich möchte aber schon eine Prüfung vom Compiler erreichen.

Ich möchte einfach rausfinden ob es in diesem Fall eine Alternative zur Konvention gibt.
 

adalbert

Mitglied
@Noctarius: Danke für die Ideen für die Klassen suche. Meine variante scheint mir etwas langsam zu sein, auch wenn sie funktioniert. Bei Zeit werde ich die mal einbauen.

@Landei: Jap, die Seite kenne ich. Davon inspiriert habe ich meine Variante mit der statischen Methode erstellt. Allerdings funktioniert der Source aus dem Quellcode nicht. Ich habe ihn 1:1 kopiert und getestet, es wird jedoch keine Compilerwarnung erzeugt.
 

Noctarius

Top Contributor
Mit einem Maven- oder Ant-Plugin lässt sich auch eine Überprüfung erreichen aber eben nur explizit nicht vom Compiler selbst.

Zur Class-Suche:
Das Problem ohne eigenen Classloader für die Suche der Klasse ist:
- Alle Klassen bleiben im Speicher (sie wurden ja geladen und werden erst entfernt wenn der Classloader weg ist und keine Referenzen mehr bestehen)
- Eventuelle Sicherheitsfeatures wie Classloaderabtrennung machen später Probleme weil die Class schon von einem höheren Classloader geladen wurde
 

adalbert

Mitglied
Maven kann ich für dieses Projekt nicht benutzen, ant jedoch wird bereits mit genutzt.
Wie könnte eine Lösung mit ant funktionieren? Mir fällt nur ein mit ant den APT aufzurufen, welcher dann aber das gleiche Problem wie der annotation processor haben dürfte.
 

adalbert

Mitglied
Danke euch beiden für die schnellen Antworten!

Ich werd mich morgen dann mit ant auseinander setzen und versuchen diese Prüfung zu realisieren.

Ob jetzt statische Methode oder Default-Konstruktor genutzt wird ist mir zemlich egal (zumal die statische Methode meist eh nur den (privaten) Default-Konstruktor aufruft).

Aber jetzt hau ich mich erstmal aufs Ohr :)
 

adalbert

Mitglied
Es ist doch möglich in Klassen über Annotations einen default Constructor (oder was auch immer) durch den Compiler zu erzwingen.

So sieht jetzt die process-methode des zugehörigen processors aus (falls es jemand interessieren sollte):
Java:
@Override
	public boolean process(Set<? extends TypeElement> annotations,
			RoundEnvironment roundEnv) {

		for (TypeElement type : annotations) {
			for (Element element : roundEnv.getElementsAnnotatedWith(type)) {
				checkClass(element);
			}
		}

		return true;
	}

	private void checkClass(Element element) {
		boolean found = false;

		if (element.getModifiers().contains(Modifier.ABSTRACT)) {
			found = true;
		} else {
			for (Element subelement : element.getEnclosedElements()) {
				if (subelement.getKind() == ElementKind.CONSTRUCTOR) {
					if (subelement.getModifiers().contains(Modifier.PUBLIC)) {
						
						TypeMirror mirror = subelement.asType();
						if (mirror.accept(noArgsVisitor, null)) {
							found = true;
							break;
						}
					}
				}
			}
		}

		if (!found) {
			this.processingEnv.getMessager().printMessage(
					Diagnostic.Kind.ERROR,
					"Public no-args constructor not implemented.", element);
		}
	}

	private static final TypeVisitor<Boolean, Void> noArgsVisitor = new SimpleTypeVisitor6<Boolean, Void>() {
		public Boolean visitExecutable(ExecutableType t, Void v) {
			return t.getParameterTypes().isEmpty();
		}
	};

Das Beispiel aus dem Link war also doch fast lauffähig :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J Die statische Main-Methode ändert Instanzvariable? Java Basics - Anfänger-Themen 10
B Attribute eines Objekts einer Klasse durch statische Methode einer 2. Klasse ändern? Java Basics - Anfänger-Themen 32
C Größte Zahl aus einem Array ermitteln(als statische Methode) Java Basics - Anfänger-Themen 31
B Statische Methode return funktioniert nicht. Java Basics - Anfänger-Themen 19
N Auf statische Methode zugreufen Java Basics - Anfänger-Themen 9
R Methoden Nicht statische Methode aus Main aufrufen Java Basics - Anfänger-Themen 2
B Java Programm ohne statische Main Methode aufrufen Java Basics - Anfänger-Themen 5
A Nicht-statische Methode in einer statischen aufrufen Java Basics - Anfänger-Themen 10
L Methoden Auf statische Methode einer anderen Klasse zugreifen, die Array zurückgibt Java Basics - Anfänger-Themen 3
S statische Methode nebenläufig Java Basics - Anfänger-Themen 2
R Aufruf statische Methode Java Basics - Anfänger-Themen 7
C Relativer Pfad - Statische Methode Java Basics - Anfänger-Themen 6
H Statische generische Methode Java Basics - Anfänger-Themen 2
K nicht-statische Methode aufrufen Java Basics - Anfänger-Themen 3
P nicht statische methode instantiieren Java Basics - Anfänger-Themen 7
K statische variablen und methode Java Basics - Anfänger-Themen 3
M Konstruktor eine statische Methode? Java Basics - Anfänger-Themen 9
F Statische Methode - Nicht Statische Methode Java Basics - Anfänger-Themen 10
M Warum dürfen Objekte einer Klasse auf statische Variablen dieser Klasse referenzieren? Java Basics - Anfänger-Themen 10
V Variablen statische Variable einer Objektvariable zuordnen Java Basics - Anfänger-Themen 3
S Klassen statische Objekterzeugung vor Konstruktoraufruf??? Java Basics - Anfänger-Themen 6
Queiser Nicht statische Klassen Java Basics - Anfänger-Themen 6
C nicht statische Methoden Java Basics - Anfänger-Themen 4
D statische generische Methoden Java Basics - Anfänger-Themen 3
D Statische Variablen/Methoden Java Basics - Anfänger-Themen 3
S Zufallszahl (Statische Attribute und Methoden) Java Basics - Anfänger-Themen 10
F Privater Konstruktor und statische Methoden Java Basics - Anfänger-Themen 4
S ActionListener und Statische Variablen Java Basics - Anfänger-Themen 4
D Statische Objekte mit variablem Parameter Java Basics - Anfänger-Themen 1
K Wieso muss man finale statische Variablen sofort oder eben im Konstruktor initialisieren? Java Basics - Anfänger-Themen 2
F Statische Klasse => Flaschenhals? Java Basics - Anfänger-Themen 10
T Statische Arrays von Objekten Java Basics - Anfänger-Themen 2
S Java Fragen Konstruktor & Statische Methoden Java Basics - Anfänger-Themen 4
S Datentypen nicht lineare STATISCHE Datenstruktur? Java Basics - Anfänger-Themen 10
E statische Variable ändert sich Java Basics - Anfänger-Themen 7
A Statische Variable in Methoden Java Basics - Anfänger-Themen 7
P Klassen statische oder dynamische(?) Klasse Java Basics - Anfänger-Themen 3
M Wann statische Methoden/Attribute? Java Basics - Anfänger-Themen 2
M Statische Methoden in Interface/Abstrakte Klasse Java Basics - Anfänger-Themen 6
J statische Methoden auf eine LinkedList initialisieren? Java Basics - Anfänger-Themen 5
A statische Arraylist Java Basics - Anfänger-Themen 6
Q Variablen Statische Variablen Java Basics - Anfänger-Themen 8
J Unterschied zwischen statische und nicht statische Methoden? Java Basics - Anfänger-Themen 14
V OOP Statische Klassen-Attribute vererben Java Basics - Anfänger-Themen 4
K Statische Bindung Java Basics - Anfänger-Themen 6
B dynamische/statische Typen Java Basics - Anfänger-Themen 2
M Statische Methoden Java Basics - Anfänger-Themen 22
sqsh statische jlabels dynamisch verwalten Java Basics - Anfänger-Themen 2
S Statische Klassen/ Singleton Java Basics - Anfänger-Themen 13
E Statische Member können nicht vererbt werden? Java Basics - Anfänger-Themen 10
F Generische Typen auch für statische Methoden? Java Basics - Anfänger-Themen 13
B statische Variable Java Basics - Anfänger-Themen 10
M Statische und nicht-statische Funktionen: Desktop.browse(uri); Java Basics - Anfänger-Themen 4
A Stilfrage: statische Methoden und Attribute auf jeden Fall verhindern? Java Basics - Anfänger-Themen 5
A Stilfrage: statische Variable mit Instanz der gleichen Klasse Java Basics - Anfänger-Themen 8
A statische Attribute: Vererbung und Zugriff darauf Java Basics - Anfänger-Themen 15
hdi Observer als statische Klasse ? Java Basics - Anfänger-Themen 2
hdi statische synchronisation Java Basics - Anfänger-Themen 6
G statische ArrayList? Java Basics - Anfänger-Themen 8
K Statische Variablen von Objekten im Array Java Basics - Anfänger-Themen 15
S statische variable initialisieren mit exception Java Basics - Anfänger-Themen 2
G statische Variable zugreifen bzw. setzen Java Basics - Anfänger-Themen 6
T in statischem Kontext auf nicht statische Variable beziehen Java Basics - Anfänger-Themen 5
M Statische Funktion Java Basics - Anfänger-Themen 2
M öffentliche nicht-statische Funktion fremder Klasse ausführn Java Basics - Anfänger-Themen 16
C args[] als statische Variablen speicher oder wie? Java Basics - Anfänger-Themen 12
H statische methoden und sichtbarkeit Java Basics - Anfänger-Themen 13
nadoria statische Methoden (Klassenmethoden) Java Basics - Anfänger-Themen 3
H Was ist nocheinmal eine statische Klasse? Java Basics - Anfänger-Themen 6
G Statische Methoden? Java Basics - Anfänger-Themen 2
kb statische methoden und throws exception Java Basics - Anfänger-Themen 2
H statische, dynamischer Typ von Variablen Java Basics - Anfänger-Themen 1
H statische,dynamische Bindung Java Basics - Anfänger-Themen 4
N Unterschied statische Attribute u. Methoden <-> objekt Java Basics - Anfänger-Themen 4
O nicht-statische Inhalte auf statische Inhalte verweisen Java Basics - Anfänger-Themen 19
M wann statische klassen? Java Basics - Anfänger-Themen 14
U statische Variablen Java Basics - Anfänger-Themen 12
S Statische Felder - statische Methoden Java Basics - Anfänger-Themen 2
D Statische und Nicht-Statische Methoden Java Basics - Anfänger-Themen 7
K Statische Methoden!? Java Basics - Anfänger-Themen 8
T Rekursive Methode Java Basics - Anfänger-Themen 13
Ü Methode soll Quadrat aus der Summer zurückgeben Java Basics - Anfänger-Themen 10
P Objekt einer Methode eines anderen Objektes übergeben Java Basics - Anfänger-Themen 5
Leyla Spezifischte Methode Java Basics - Anfänger-Themen 16
M Methode zielnah zeigt das gewünschte Ausgabe nicht an Java Basics - Anfänger-Themen 3
L Variablenwerte aus einer Methode übergeben Java Basics - Anfänger-Themen 2
T Methode soll etwas ausrechnen und zurückgeben (klappt nd) hat wer eine Idee? Java Basics - Anfänger-Themen 11
P Main Methode scheint Constructor aufzurufen, ohne dass es so gecoded ist Java Basics - Anfänger-Themen 2
T Aufruf der Methode einer Oberklasse, wenn sie in der Unterklasse überschrieben ist. Polymorphie. Java Basics - Anfänger-Themen 2
C Zugriff auf Methode Java Basics - Anfänger-Themen 2
M Datentypen While-Schleife eine Java Methode erstellen Java Basics - Anfänger-Themen 3
T Methode akzeptiert String nicht Java Basics - Anfänger-Themen 18
M Methode sperren bis ein Kriterium erfüllt wurde Java Basics - Anfänger-Themen 3
D Switch Case Methode aufrufen Java Basics - Anfänger-Themen 3
C Unbekannte Methode add bei Klasse die JTree erweitert Java Basics - Anfänger-Themen 14
M methode aufrufen ohne parameter Java Basics - Anfänger-Themen 1
marcelnedza Finde meinen Fehler in einer Methode nicht, Java Karol Java Basics - Anfänger-Themen 15
monsterherz einfache Methode mit Fehler den ich nicht finde Java Basics - Anfänger-Themen 21
Ostkreuz Wieso wird die Methode nochmal aufgerufen? Java Basics - Anfänger-Themen 5
G Variable aktualisiert sich nicht in rekursiver Methode Java Basics - Anfänger-Themen 4

Ähnliche Java Themen

Neue Themen


Oben