Generics + Vererbung

White_Fox

Top Contributor
Einen schönen Sonntag allerseits.

Folgendes Problem: Ich habe hier eine Klasse, die mit generischen Datentypen arbeitet:

Java:
public abstract class TopLevelClass<T>{
    private T t;
    public void setT(T t){This.t = t;}
    public T getT(){return this.t}
}

Diese Klasse erweitere ich jetzt:
Java:
public class TopLevelWithName extends TopLevelClass{
    private String name;
    public void setName(String s){this.name = s;}
    public String getName(){return name;}
}

Soweit ich das sehe, kompilieren beide Klassen. Was ich mich jetzt allerdings frage: Woher weiß die Superklasse von TopLevelWithName jetzt, was sich hinter T verbirgt?
Java:
void method(){
    TopLevelClass<Integer> top;                     //Compiler erzwingt die Angabe eines Typs zwischen den <>
    TopLevelClassWithName topWithName;   //...und was ist hier?
}

Was ich am Ende haben will ist eine abstrakte Klasse. Abstrakt deshalb, weil der Nutzer diese selber für seinen Anwendungsfall erweitern soll. Klasse anstatt Interface deswegen, weil ich einem Interface kein Verhalten beibringen kann (dies erfordert Instanzvariablen).
Jetzt will ich erzwingen, daß der Nutzer der abstrakten Klasse aber einen Datentypen festlegen und diesen meiner abstrakten Klasse mitteilen muß.
Ich habe schon an einen Parameter public ClassConstructor(Class<Type> T) gedacht, aber gibt es da vielleicht etwas direkt auf Compilerebene, sodaß der Nutzer den Diamondoperator ebenfalls nutzen kann ohne ein Class-Objekt durch alle Konstruktoraufrufe durchzuschleifen?
 

httpdigest

Top Contributor
Deine TopLevelWithName Klasse erweitert zwar TopLevelClass, aber gibt kein Typargument für dessen Typparameter <T>. Deine TopLevelWithName erweitert die Raw-Ausprägung der TopLevelClass. Das heißt, T=Object.
Du führst dann noch ein weiteres Feld `name` und entsprechende Setter und Getter ein. Das heißt, deine TopLevelWithName Klasse besitzt dann zwei Felder (`name` und `t` - wobei `t` nur sichtbar in der Oberklasse ist) und besitzt vier Methoden (die Getter/Setter `getName`/`setName` sowie `getT`/`setT`). Das willst du vermutlich nicht.
Du wolltest vielleicht folgendes schreiben:
Java:
public class TopLevelWithName extends TopLevelClass<String> {
}
 

White_Fox

Top Contributor
Deine TopLevelWithName Klasse erweitert zwar TopLevelClass, aber gibt kein Typargument für dessen Typparameter <T>.
Kann ich diese Weitergabe irgendwie forcieren? Das Ganze funktioniert, wenn die erweiternde Klasse mit public class TopLevelWithName<T> extends TopLevel<T>{} deklariert wird. Aber diese Deklaration liegt im Ermessen desjenigen, der von der Klasse ableitet-und diesen Ermessensspielraum würde ich gerne eingrenzen.

Du führst dann noch ein weiteres Feld `name` und entsprechende Setter und Getter ein. Das heißt, deine TopLevelWithName Klasse besitzt dann zwei Felder (`name` und `t` - wobei `t` nur sichtbar in der Oberklasse ist) und besitzt vier Methoden (die Getter/Setter `getName`/`setName` sowie `getT`/`setT`). Das willst du vermutlich nicht.
Ja, dafür ist das Beispiel etwas ungünstig, grundsätzlich will ich aber schon daß die Subklasse von t nix mehr mitbekommt.
 

ocsme

Top Contributor
Ich glaube was @mihe7 meint ist das hier:

Nun bringt mich das Thema hier aber auch zu einer Frage.
Wenn man eine Generische Klasse hat die zu einem Generischen Typ wird bei Konkretisierung des Typs, wenn ich diese ableite und nehme bei der Ableitung den RAW Typ macht das Sinn?
Ich kann dabei kein Anwendungsbeispiel finden und stehe deswegen auch mega auf dem Schlauch dachte ich hätte das so halb wegs verstanden und jetzt werfe ich wieder alles durcheinander :(
 

White_Fox

Top Contributor
Ich bin mir nicht so sicher welchen Link mir mihe7 wirklich geben wollte. Aber in seinem Link hab ich auch die Multiple Bounds gefunden-das ist mir auch sehr nützlich.

Was ich halt einfach gerne hätte ist, daß 2. einfach gleich rot markiert wird und der Code gar nicht erst zu schreiben ist. Das sieht augenscheinlich sonst erstmal so aus, als würde das gehen. Oder ist das normal, daß der Compiler das nicht gleich prüft?
Java:
public abstract class TopLevelClass<T>{
    private T t;
    public void setT(T t){This.t = t;}
    public T getT(){return this.t}
}

// 1. Hier ist alles gut.
public class PoorlyTopLevelWithName<T> extends TopLevelClass<T>{
    private String name;
    public void setName(String s){this.name = s;}
    public String getName(){return name;}
}

// 2. Das soll der Compiler mir bitte um die Ohren hauen während ich das so schreibe.
public class BetterTopLevelWithName extends TopLevelClass{
    private String name;
    public void setName(String s){this.name = s;}
    public String getName(){return name;}
}
 

httpdigest

Top Contributor
Mal dumme Frage: Warum genau stört es dich, wenn ein Client deinen (Library-)Code raw verwendet und kein Typargument angibt? Prüfst du zur Laufzeit den reified Type per Reflection getGenericSuperclass() oder sowas und holst dir das Typargument zur Laufzeit? Was ich nur meine: Was für einen Unterschied macht es denn für dich?
 

White_Fox

Top Contributor
Was ich nur meine: Was für einen Unterschied macht es denn für dich?
Ich schreibe mir gerade eine kleine Bibliothek (siehe Nachbarthread in Softwareentwicklung), und zu deren Verwendung gehört, daß man eine Klasse für seine Zwecke ableiten muß. Ich befürchte da Probleme zur Laufzeit, wenn man da einfach wild mit Object arbeitet-außer, man will es.

Außerdem ist es auch eine Komfortfrage. Der Benutzer soll Objekte einlegen und wieder entnehmen können, ähnlich wie z.B. bei einer ArrayList oder anderen Collections. Ansonsten wird das ein wildes Herumgecaste...das ist Mist.


Ja, aus Gründen der Rückwärtskompatibilität.
Also bleibt nichts außer gründlicher Dokumentation...richtig?
 

White_Fox

Top Contributor
Och mihe7...ich hab doch gerade mal die Generics so halbwegs verstanden (irgendwo stand daß das schon zu den komplizierteren Dingen in Java gehört), jetzt haust du mir schon wieder so einen Brocken um die Ohren. :(

Ich hab mir mal das Tutorial zu Annotations von Oracle angesehn, aber ich hab keine Idee wie ich mir das zunutze machen kann. Hast du einen Tipp?
 

White_Fox

Top Contributor
Mensch mihe7...dankeschön. :)

Eine Frage noch:
Wie funktiniert das Ding? Ich will ja auch dazulernen. :) Der Code ist ja von der Menge her recht übersichtlich, und wie eine eigene Annotation geschrieben wird ist von Oracle gut erklärt. Aber die meisten, die etwas über AnnotationProcessing auf StackOverflow gefragt haben, scheinen da nicht bei null angefangen zu haben.

Ansonsten:
Fall jemand allgemein ein gutes Tutorial sucht, DuckDuckGo hat dann doch noch eins für mich gefunden (sogar auf deutsch):

Damit hab ich zumindest einen Teil von mihe7s Prozessor verstanden. :)
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Das ist auch für mich alles Neuland. Der Annotation Processor ist eine Implementierung des Interfaces Processor, hier in Form einer Implementierung der abstrakten Basisklasse AbstractProcessor. Die Verarbeitung von Annotationen lässt sich über verschiedene Wege starten, z. B. über das Tool apt (im JDK) oder auch direkt über den Compiler (wie hier). Dazu wird die Implementierung über den Service Loader Mechanismus registriert (im Jar unter META-INF/services eine Datei mit dem FQN der zu implementierenden Schnittstelle anlegen, die als Inhalt den FQN der Implementierung enthält). Im Projekt findest Du die Datei unter src/extras/services (und nicht wie unter Maven üblich unter src/main/resources/META-INF/services) , weil es unter Maven sonst zu einem Henne-Ei-Problem kommt. Daher auch der Spaß via github.

Dadurch, dass die Jar im Classpath liegt, werden vom Java Compiler automatisch Annotationen unter Verwendung von GenericClassProcessor (GCP) verarbeitet.

Während der Verarbeitung arbeitet man mit einem anderen Modell, als das, was man sonst von java.lang/java.lang.reflect her kennt, weil die Klassen zum Zeitpunkt der Verarbeitung noch nicht geladen sind.

Im Wesentlichen bekommt die process-Methode einen Satz von Annotationen, der Processor kann nun fragen, welche Typen diese Annotationen implementieren (ich hoffe mal, dass nur Annotationen von den mit @SupportedAnnotationTypes genannten Typen übergeben werden - sonst ist der Code falsch).

Java:
        Set<? extends Element> annotated = annotations.stream()
            .flatMap(a -> roundEnv.getElementsAnnotatedWith(a).stream())
            .collect(Collectors.toSet());

Das wären in diesem Fall (bzw. sollten sein) die mit @GenericClass annotierten Klassen. Im zweiten Schritt hole ich mir die "root elements", das sind - soweit ich das verstanden habe - alle in den vom Processor zu verarbeitenden Klassen/Typen/etc., die kompiliert werden sollen. Jedes Element wird überprüft, ob es eine direkte Subklasse einer annotierten Klasse (annotated) ist und falls ja, ob Typ-Argumente angegeben wurden. Ist die Liste der Typ-Argumente leer, wird ein Fehler ausgegeben.

Java:
        roundEnv.getRootElements().stream()
            .filter(TypeElement.class::isInstance)
            .map(TypeElement.class::cast)
            .filter(e -> e.getTypeParameters().isEmpty())
            .filter(e -> annotated.contains(((DeclaredType) e.getSuperclass()).asElement()))
            .filter(e -> ((DeclaredType) e.getSuperclass()).getTypeArguments().isEmpty())
            .forEach(e -> {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                   String.format("%s tries to implement %s without specifying type parameters",
                       e.getSimpleName().toString(),
                       ((DeclaredType) e.getSuperclass()).asElement().toString()),
                   e);
            });
 

White_Fox

Top Contributor
So...da wäre ich nochmal: Ich Netbeans leider nicht dazu überredet bekommen, das, was ich von Github runtergeladen habe, als Annotationsprozessor zu akzeptieren. Ich bin die entsprechende Anleitung schrittweise durchgegangen, aber Netbeans meckerte ständig.
Ich bin dann das Tutorial nochmal durchgegangen (inkl. Prozessor- und Interfacedeklaration, das ansich ist ja noch easypeasy) und hab selbst einen Prozessor nebst Interface deklariert.

Das ist zwar nix worauf ich stolz sein kann, aber immerhin frißt Netbeans den Prozessor und die Annotaion jetzt so, daß ich diese in meiner Klasse verwenden kann. Außerdem hab ich diese jetzt in einem Subpackage direkt am Ort der Verwendung. Ich überlege ob ich das als eigenständige Bibliothek auslagere, da finde ich das schon praktisch wenn der AP direkt drin sitzt und ich nicht nochmal nach externen Quellen verlinken muß. Und naja...da kann ich den Rest noch selber schreiben.

Jetzt will ich aber auch deinen Code nicht schamlos kopieren, wenigstens genau verstehen was der da macht. Code einbauen den man nichtmal nachvollziehen kann...sowas kann ich ja gar nicht leiden.
Allerdings komme ich mit den funktionalen Ausdrücken nicht klar, ich hab keine Ahnung was das heißen soll. Könntest du das vielleicht bitte noch näher erläutern?

Wo hast du eigentlich die Informationen her, was da wie in RoundEnviroment übergeben wird? Gibt es da von Oracle irgendeine Übersicht oder so?
 

mihe7

Top Contributor
Wo hast du eigentlich die Informationen her, was da wie in RoundEnviroment übergeben wird?
Das habe ich mir mühselig zusammengesucht und getestet.

Allerdings komme ich mit den funktionalen Ausdrücken nicht klar, ich hab keine Ahnung was das heißen soll. Könntest du das vielleicht bitte noch näher erläutern?
Logisch:
Java:
Set<? extends Element> annotated = annotations.stream()
            .flatMap(a -> roundEnv.getElementsAnnotatedWith(a).stream())
            .collect(Collectors.toSet());
ist etwa wie
Java:
Set<? extends Element> annotated = new HashSet<>();
for (TypeElement annotation : annotations) {
    for (Element elem : annotation.getElementsAnnotatedWith(annotation)) {
        annotated.add(elem);
    }
}
und
Java:
        roundEnv.getRootElements().stream()
            .filter(TypeElement.class::isInstance)
            .map(TypeElement.class::cast)
            .filter(e -> e.getTypeParameters().isEmpty())
            .filter(e -> annotated.contains(((DeclaredType) e.getSuperclass()).asElement()))
            .filter(e -> ((DeclaredType) e.getSuperclass()).getTypeArguments().isEmpty())
            .forEach(e -> {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                   String.format("%s tries to implement %s without specifying type parameters",
                       e.getSimpleName().toString(),
                       ((DeclaredType) e.getSuperclass()).asElement().toString()),
                   e);
            });
ist in etwa (hoffe, ich habe mich nirgends mit den Klammern vertan):
Java:
for (Element elem : roundEnv.getRootElements()) {                                   // stream()
    if (elem instanceof TypeElement) {                                              // filter
        TypeElement e = (TypElement) elem;                                          // map
        if (e.getTypeParameters().isEmpty() &&                                      // filter
                annotated.contains((DecaredType) e.getSuperclass()).asElement()) && // filter
                ((DeclaredType) e.getSuperclass()).getTypeArguments().isEmpty()) {  // filter
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,         // forEach
                   String.format("%s tries to implement %s without specifying type parameters",
                       e.getSimpleName().toString(),
                       ((DeclaredType) e.getSuperclass()).asElement().toString()),
                   e);
        }
    }
}

Willst Du eine Erklärung zu den Streams im Allgemeinen?
 
X

Xyz1

Gast
TopLevelClass<Integer> top; //Compiler erzwingt die Angabe eines Typs zwischen den <> TopLevelClassWithName topWithName; //...und was ist hier?
Du kannst in beiden Fällen raw types verwenden. Nachzulesen hier:

Wenn die abgeleitete Klasse nicht parametrisiert wurde, ist es die übergeordnete auch nicht.
Wenn die übergeordnete Klasse nicht parametrisiert wurde, darf die abgeleitete Klasse auch nicht parametrisiert werden.

Man hat sich für raw types entschieden um die Abwärtskompatibilität zu gewährleisten.
 
X

Xyz1

Gast
Wenn die übergeordnete Klasse nicht parametrisiert wurde, darf die abgeleitete Klasse auch nicht parametrisiert werden.
o_O was schreib ich...

Gegeben sei:
Java:
class A<T> {
	
}

class B<T> extends A<T> {
	
}

Java:
	public static void main(String[] args) {
		A a = new A();
		A b = new B();
		A<String> c = new A();
		A<String> d = new B();
		A e = new A<String>();
		A f = new B<String>();
		A<String> g = new A<String>();
		A<String> h = new B<String>();
		
		//B i = new A();
		B j = new B();
		//B<String> k = new A();
		B<String> l = new B();
		//B m = new A<String>();
		B n = new B<String>();
		//B<String> o = new A<String>();
		B<String> p = new B<String>();
		
		//A<String> q = new A<Integer>();
	}

ist erlaubt
 

White_Fox

Top Contributor
Das habe ich mir mühselig zusammengesucht und getestet.
Nachts um drei? Und da produzierst du noch kompilierfähigen Code? Eiderneieiei...

Willst Du eine Erklärung zu den Streams im Allgemeinen?
Danke, nein, ich denke mit Streams kenne ich mich halbweg aus. Auch wenn ich selber an dieser Stelle nie auf die Idee gekommen wäre einen Stream zu verwenden und tatsächlich eher geschachtelte for-Schleifen (vermutlich in mehreren Methoden ausgelagert) geschrieben hätte. :)

Vielen Dank für die Erklärung...das macht es sehr verständlich. Es ist noch nichtmal Mittag, und ich hab wieder etwas gelernt-der Tag war nicht umsonst. :)
Ich sehs schon kommen, mihe7 macht aus mir nochmal einen Softwareentwickler...:eek:

@Tobias-nrw
Ja, ich denke das mit der Abwärtskompatibilität hab ich halbwegs verstanden, allerdings will ich diese hier keineswegs haben, da dann Probleme zu erwarten sind.
 
X

Xyz1

Gast
Das stimmt wohl, raw types sollte man vermeiden dennoch gibt es die Möglichkeit :) ... Aber zwingt dich ja keiner nicht parametrisierte Typen zu verwenden...
 

White_Fox

Top Contributor
@mihe7
Ich weiß nicht, wo das Problem lag. Jedenfalls hat er die Annotation nicht nicht gefunden und diese stets rot markiert, und beim Codehacken hat er diese auch nicht vorgeschlagen.

Ich denke aber nicht, daß es an deinem Code lag. Wahrscheinlicher ist, daß ich doch noch irgendwo etwas übersehen habe.

@Tobias-nrw
Das stimmt schon, MICH zwingt niemand dazu. Aber ich will den Nutzer (also vornehmlich mich selbst, also werd ich ja doch wieder gezwungen :eek: ) zwingen, die Generics mitzuschleifen.
Wenn ich irgendwann mal den Code wiederverwenden will und absolut keine Ahnung mehr hab auf was ich da achten muß, will ich nicht in folgendes Problem laufen:

Code:
public class ModifiedArrayList extends ArrayList{  //Anstatt: ModifiedArrayList<T> extends ArrayList<T>
    //...
}

public static main(String Args[]){
    ModifiedArrayList list = new ModifiedArrayList();
    SpecialClass complexObject = new SpecialClass();
    
    list.add(complexObject); //complexObject ist ja vom Typ Object
    
    //...

    list.get(i).rufeSpecialClassMethodeAuf(); //...und der Compiler so: Nöööö!
    //Alternativ: Wildes Herumgecaste
}

Sicher, ich kann mich auch hinstellen und sagen, nur ich verwende daß, ich weiß ja was ich gemacht habe. Dann bleibt es aber immer noch potentiell fehleranfälliges Design, und damit in meinen Augen schlechtes Design. Und auf schlechtes Design hab ich keine Lust, die Welt ist schlecht genug, man muß sie durch schlechte Software nicht noch schlechter machen.

Und selbst wenn ich für alle Ewigkeit weiß was ich da gemacht habe (Illusion): Vielleicht gebe ich mein Werk ja mal weiter, da hab ich dann keine Lust viel erklären zu müssen.
 

White_Fox

Top Contributor
Mal eine Frage zur Syntax:
Java:
if(e instanceof TypeElement){
    e = (TypeElement) e;
    if(e.getTypeElements().isEmpty()){

    }
}

Wieso macht er mir die getTypeElement()-Methode rot und sagt "Can not find symbol: symbol: getTypeElement Location: e variable of TypeElement"? Der Import import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; ist drin.
Ich glaube ich verstehe aber so langsam, was die Methode genau macht. Respekt daß du das in so kurzer Zeit herausgefunden hast, mihe7.
 
K

kneitzel

Gast
Mal eine Frage zur Syntax:
Java:
if(e instanceof TypeElement){
    e = (TypeElement) e;
    if(e.getTypeElements().isEmpty()){

    }
}

Wieso macht er mir die getTypeElement()-Methode rot und sagt "Can not find symbol: symbol: getTypeElement Location: e variable of TypeElement"? Der Import import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; ist drin.
Ich glaube ich verstehe aber so langsam, was die Methode genau macht. Respekt daß du das in so kurzer Zeit herausgefunden hast, mihe7.

Du veränderst doch nicht den Typ einer Variable. Der Konstrukt e = (TypeElement) e; verändert also nichts.

Du solltest statt dessen Code wie den folgenden schreiben:
Java:
if(e instanceof TypeElement){
    TypeElement te = (TypeElement) e;
    if(te.getTypeElements().isEmpty()){

    }
}
Sprich: Du deklarierst eine neue Variable mit dem korrekten Typ und nutzt diese dann.
 

mihe7

Top Contributor
Wenn ich irgendwann mal den Code wiederverwenden will und absolut keine Ahnung mehr hab auf was ich da achten muß, will ich nicht in folgendes Problem laufen:
Die Frage wäre, ob das wirklich ein Problem darstellt. Wenn Du eine Klasse ohne Typparameter implementieren würdest, dann würdest Du automatisch mit Object arbeiten müssen. Spätestens dann ist aber klar, dass das entweder unabsichtlich passierte oder aber gewollt war.

Anders formuliert: dieses Problem hat sich irgendwie noch keinem gestellt :)
 

White_Fox

Top Contributor
Anders formuliert: dieses Problem hat sich irgendwie noch keinem gestellt :)
Nun, das wiederum mag ich nicht beurteilen. Ich fand das aber schon unschön, als ich den Unittest für die Klasse, für die ich das benutzen will, geschrieben habe.

Aber mal was anderes:
Ich denk ich hab den Code soweit adaptiert. Das kam dabei raus:
Java:
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.tools.Diagnostic;

@SupportedAnnotationTypes("ForceUsingDeclaredGenerics")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ForceUsingDeclaredGenericsProcessor extends AbstractProcessor{
    
    private HashSet<Element> getAnnotatedElementsInEnviroment(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
        HashSet<Element> elements = new HashSet<>();
        for(TypeElement annotation : annotations){
            for(Element e : roundEnv.getElementsAnnotatedWith(annotation)){
                elements.add(e);
            }
        }
        return elements;
    }
    
    private boolean declarationTypeIsMissing(TypeElement e, HashSet<Element> elements){
        return e.getTypeParameters().isEmpty() &&
                elements.contains(((DeclaredType) e.getSuperclass()).asElement()) &&
                ((DeclaredType) e.getSuperclass()).getTypeArguments().isEmpty();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        HashSet<Element> annotatedElements = getAnnotatedElementsInEnviroment(annotations, roundEnv);
        String message;
        TypeElement te;
        
        for(Element e : roundEnv.getRootElements()){
            if(e instanceof TypeElement && declarationTypeIsMissing((TypeElement) e, annotatedElements)){
                te = (TypeElement) e;
                message = String.format("%s extends %s without defining generic type definition.",
                        te.getSimpleName().toString(),
                        ((DeclaredType) te.getSuperclass()).asElement().toString());
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message);
            }
        }
        return true;
    }
}


@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface ForceUsingDeclaredGenerics {}

Wenn ich den Unittest für die betreffende Klasse schreibe, kann ich die Generics immer noch übergehen ohne einen Fehler zu erhalten. Ich hab mal ein System.out.println-Anweisung in den Prozessor eingebaut-die kam auf der Ausgabe auch nicht.
Ich habe testweise auch den Code original von mihe7 drin,

Die Klasse(n), wo die Annotation verwendet werden soll, ist wie folgt deklariert:
Java:
@ForceUsingDeclaredGenerics
abstract class TableLine<IT extends Itemable> { //Itemable ist ein Interface, daß Items implementieren müssen
    
    private ArrayList<IT> items;

    public TableLine() {
        this.items = new ArrayList<>();
    }
    
    void addElement(IT item){
        items.add(item);
    }
    
    public int getLenght(){
        return items.size();
    }

    public IT getItem(int i){
        return (i >= items.size() || i < 0) ? null : items.get(i);
    }
}


@ForceUsingDeclaredGenerics
public abstract class Row<RT extends RowHeader, IT extends Itemable> extends TableLine<IT>{
    RT rowheader;
    
    public Row(){
        this.rowheader = null;
    }

    public Row(RT rowheader){
        this.rowheader = rowheader;
    }
    
    public RT getRowheader(){
        return null;
    }

Der Test der Klasse Row:
Java:
public class RowTest {
    
    public RowTest() {
    }
    
    class ExtendedRow extends Row{      //Hier erwarte ich Ärger und kriege keinen
        
        public ExtendedRow(RowHeader rowheader) {
            super(rowheader);
        }
    }
    
    class RowItem implements Itemable{

        @Override
        public <T extends ItemModel> T getItemModel(Class<T> m) {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }

        @Override
        public void takeTableinformation(RowHeader row, ColumnHeader col) {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }
    }
    
    class ExtendedRowheader extends RowHeader{
        
    }
    
    /**
     * Test of getRowheader method, of class Row.
     */
    @Test
    public void testGetRowheader() {
        Row instance;
        
        System.out.println("getRowheader");
        
        instance = new ExtendedRow(new ExtendedRowheader());
        
        // TODO review the generated test code and remove the default call to fail.
        fail("The test case is a prototype.");
    }
}

Hast du noch eine Idee, mihe7? Wenn ich mir die Javadocs so ansehe (insbesondere zu den Annotationen über dem Annotations-@Interface) finde ich kein Problem. Hast du das mal ausprobiert?
 

White_Fox

Top Contributor
Ja, hab ich. Wobei...ich hab den ja als Quellcode (noch) in meinem Projekt drin. Kann es sein, daß der Compiler den zu spät kompiliert? Ich meine, ich hab da irgendwas in der Richtung irgendwo gelesen. Irgendeine Compileroption, mit der man den Build verzögern kann oder so. Ich komm aber nicht mehr drauf, wo das war.
 

White_Fox

Top Contributor
Ich hab den Prozessor mal in eine eigene Klassenbibliothek verschoben, die Bibliothek erstellt, in mein Projekt eingebunden und den Prozessor annotiert.

Irgendwas haut da noch nicht so richtig hin, jetzt findet er die Deklaration nicht mehr. Merkwürdig.
 

mihe7

Top Contributor
Kann es sein, dass die Basisklasse oder die abgeleitete Klasse keien TopLevel-Klassen sind? Mit inneren haut das aktuell nicht hin (habe ich eben festgestellt, da wird man vermutlich noch was dransetzen müssen, damit auch die Internas der Klassen überprüft werden).

Probier mal folgendes:
Java:
import gst.GenericClass;

@GenericClass
class A<T> {}

public class Test extends A {
}

Das sollte einen Fehler werfen.

Das hier:
Java:
public class Test {
    @GenericClass
    class A<T> {}
    class B extends A {}
}
läuft dagegen durch.
 

White_Fox

Top Contributor
Hm...ja, das erklärt es wohl. :(

Naja, erstmal sehen wie ich Netbeans überredet bekomme, die Bibliothek einzubinden. Manchmal ist es da zickig.
 
Zuletzt bearbeitet:

White_Fox

Top Contributor
Ich habs jetzt: In Netbeans kann man Bibliotheken auf zweierlei Arten einbinden: Einen Haufen Jars mit Klassenpfad und Javadocs als Bibliothek anmelden und diese Bibliothek dann einbinden. Oder: direkt das entsprechende Projekt einbinden.

Wenn ich die kompilierte Bibliothek (also Jars und Javadoc) einbinde, findet er die Annotation nicht. Wenn ich jedoch das Projekt selbst einbinde, dann geht es.

Hat jemand eine Idee, was Netbeans da im Hintergrund veranstaltet? Das Projekt ist ein Ant-Projekt.
 

mihe7

Top Contributor
Habe jetzt auf einem anderen Rechner folgendes gemacht:
1. das jqfa-Projekt als Zip aus github geholt, entpackt.
2. NB 11 gestartet -> keine Projekte offen
3. Das entpackte Maven-Projekt gst geöffnen -> Clean & Build
4. Neues Ant-Projekt angelegt
5. Projekteigenschaften geöffnet, dort
a) In den Libraries das unter 3. erzeugte gst-1.0-SNAPSHOT.jar zum Classpath hinzugefügt
b) Unter Build die Haken wie in Deinem Bild gesetzt
6. Die von NB unter 4. erzeugte Klasse bearbeitet.
Ergebnis:

12231
 

White_Fox

Top Contributor
@mrBrown
Ich werd den Code, für den ich das verwenden will, mal hier reinstellen wenn er fertig ist. Danach würde mich eure Meinung dazu interessieren, bzw. wie ihr damit umgegangen wärt.
 

mihe7

Top Contributor
Sollte es nicht auch reichen, die passenden Compiler-Warnungen
Oh, mein Gott... das wäre nun wirklich zu einfach.

@White_Fox die Warnung gibts von Haus aus. Übersetz mal mit -Xlint dann bekommst Du etwas wie:

Test.java:2: warning: [rawtypes] found raw type: X
public class Test extends X {}
^
missing type arguments for generic class X<T>
where T is a type-variable:
T extends Object declared in class X
2 warnings

Du kannst Dir den Annotation Processor also ruhig sparen - war trotzdem interessant :)
 

White_Fox

Top Contributor
Warnungen...sind doch da, um ignoriert zu werden. Oder nicht?

Die Lösung mit dem AP gefällt mir sehr viel besser (und wer weiß wozu das mal gut sein kann zu wissen, wie man einen AP schreibt), aber tatsächlich: Einfacher sind die Wahrnungen auf jeden Fall. :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Kirby.exe Vererbung bei Generics Allgemeine Java-Themen 7
kaoZ Generics und Vererbung Allgemeine Java-Themen 3
F Google Guice + Generics + Vererbung Allgemeine Java-Themen 5
G Generics und Vererbung. Allgemeine Java-Themen 21
J Best Practice Generics mit Enum Allgemeine Java-Themen 3
H Kombination Interface und Abstrakte Klasse bei Generics Allgemeine Java-Themen 3
Zeppi Cast Object in Generics Allgemeine Java-Themen 4
P Generics und Arrays Allgemeine Java-Themen 6
M Generics / Typen Allgemeine Java-Themen 1
Kirby.exe InsertionSort mit Generics Allgemeine Java-Themen 33
H Klarnamen etc... (von Wie Generics lernen?) Allgemeine Java-Themen 26
D Wie Generics lernen? Allgemeine Java-Themen 26
L Compiler-Fehler Generics beim Anhängen von Predicates Allgemeine Java-Themen 1
W Vererbung Generics - mal wieder die verhaßte Rückwärtskompatibilität Allgemeine Java-Themen 2
S Verstaendnisfrage Generics Allgemeine Java-Themen 19
I Methoden Generics-Methode Allgemeine Java-Themen 3
D Mit Generics arbeiten - Übungsaufgabe Allgemeine Java-Themen 3
K Factory Pattern: Mit Generics umgehen Allgemeine Java-Themen 6
G Generics Allgemeine Java-Themen 31
perlenfischer1984 Liste mit generics zurück liefern Allgemeine Java-Themen 8
Hacer Generics & Collections Allgemeine Java-Themen 8
Neumi5694 Interface Generics für Enum-Filterung verwenden Allgemeine Java-Themen 5
H Collector Generics Problem (incl. Stream & Lambda) Allgemeine Java-Themen 4
C Gemeinsame Oberklasse zweier Generics Allgemeine Java-Themen 10
erdmann Datentypen Methodendeklaration mit Generics Allgemeine Java-Themen 2
Z Datentypen Verschachtelte Generics Allgemeine Java-Themen 1
Neumi5694 Datentypen Generics Allgemeine Java-Themen 5
S Mit Generics Klasse erstellen die selbst T erweitert..? Allgemeine Java-Themen 4
Tarrew Generics - Type erasure Allgemeine Java-Themen 5
N Problem mit Generics und Interface Allgemeine Java-Themen 4
H Generics als Parameter Allgemeine Java-Themen 1
A Datentypen Generics: Wie am besten auf Typparameter zugreifen Allgemeine Java-Themen 2
C Generics Objekt in ArrayList Allgemeine Java-Themen 2
vandread Kleine Generics Aufgabe aus einer Prüfung... wie ist das gemeint? Allgemeine Java-Themen 6
G Generics sind zu streng - oder ich zu naiv? Allgemeine Java-Themen 3
G Verschachtelte Generics Allgemeine Java-Themen 2
O Generics Allgemeine Java-Themen 42
M Problem mit Generics Allgemeine Java-Themen 10
M Generics (bounded wildcards statt Interface Bezeichnern) -- Sinn oder Unsinn? Allgemeine Java-Themen 2
darekkay Generics: Wildcard und Object Allgemeine Java-Themen 5
H Collections Generics und Reflection Allgemeine Java-Themen 6
H Problem mit Java Generics Allgemeine Java-Themen 6
J Generics: Typparameter als Klasse zurückliefern Allgemeine Java-Themen 4
H Generics Allgemeine Java-Themen 5
P Probleme mit Generics Allgemeine Java-Themen 5
B Generics und primitve arrays Allgemeine Java-Themen 6
M Generics Allgemeine Java-Themen 11
1 Collections Generics, internes Verhalten Allgemeine Java-Themen 16
T Warnungsfreie Verwendung von Generics Allgemeine Java-Themen 11
M Probleme mit Generics Allgemeine Java-Themen 5
D Java Generics Allgemeine Java-Themen 8
2 Generics: bounded wildcards Allgemeine Java-Themen 4
J Generics / vermeiden von downcasts Allgemeine Java-Themen 2
2 Generics oder nicht? Allgemeine Java-Themen 8
E Problem mit Generics und Comparable Allgemeine Java-Themen 16
W Erweitern einer Klasse mit Generics Allgemeine Java-Themen 8
H Generics für Methode Allgemeine Java-Themen 14
N Überladen mit Hilfe von Generics Allgemeine Java-Themen 3
S Generics: Fuer Set<T> ein T-Klassenobjekt erhalten? Allgemeine Java-Themen 3
Q Der innere Typ von Generics? Allgemeine Java-Themen 3
N Generics-NullpointerException Allgemeine Java-Themen 7
2 Generics - Typ Allgemeine Java-Themen 12
P Generics Problem Allgemeine Java-Themen 10
S Type safety Warnings beim casten von Generics Allgemeine Java-Themen 6
N Generics Allgemeine Java-Themen 3
V Frage zu Generics Allgemeine Java-Themen 2
S java generics klassen deklaration Allgemeine Java-Themen 7
B hashtable für unterschiedliche Typen - mit Generics Allgemeine Java-Themen 8
E Generics Allgemeine Java-Themen 3
MQue Generics Allgemeine Java-Themen 4
R Problem mit Reflection und Generics Allgemeine Java-Themen 3
C Klassen, die aufeinander verweisen (mit Generics) Allgemeine Java-Themen 16
G Generics - W.card unter Nutzung von Annotationsklasse? Allgemeine Java-Themen 6
G sortieren von generics Allgemeine Java-Themen 10
G Generics in Map. Type of value abhängig vom key Allgemeine Java-Themen 3
A Generics Verständnisfrage Allgemeine Java-Themen 7
Z Generics funzt nicht? Allgemeine Java-Themen 2
T Generics Allgemeine Java-Themen 18
G ComboBox: Nur eine Art Klasse zulassen (Generics) Allgemeine Java-Themen 3
J Generics Expertenwissen? Allgemeine Java-Themen 5
S Generics-Problem Allgemeine Java-Themen 3
T Generics und Wil-dcards Allgemeine Java-Themen 8
Q Typen von Generics & Casten Allgemeine Java-Themen 3
S Generics Allgemeine Java-Themen 2
R Problem mit Generics Allgemeine Java-Themen 2
G Trotz Generics Cast-Fehler! Allgemeine Java-Themen 5
T TreeMap durch Comparator mit Generics sortieren Allgemeine Java-Themen 9
T Generics und instanceof Allgemeine Java-Themen 10
T Generics und Exceptions Allgemeine Java-Themen 6
M Beliebig viele Typen bei Generics Allgemeine Java-Themen 3
G Reflection objekt mit generics erzeugen Allgemeine Java-Themen 5
S Singleton Pattern mit Generics Allgemeine Java-Themen 4
G Generics und Comparable Allgemeine Java-Themen 11
H Generics Problem Allgemeine Java-Themen 3
F Generics: spricht etwas dagegen raw types zu verwenden? Allgemeine Java-Themen 31
M Generics - besser programmieren, Warnung umgehen Allgemeine Java-Themen 4
E Java, UML, Generics Allgemeine Java-Themen 6
P Array von Vectoren + Generics Allgemeine Java-Themen 6
M ArrayList erweitern - generics Allgemeine Java-Themen 4
E Generics -> UML Allgemeine Java-Themen 4

Ähnliche Java Themen

Neue Themen


Oben