@Tellerrand:
ich habe ehrlich gesagt nicht bewusst darauf geachtet, dass bei Printable-Objekten nicht zwischen den Untertypen von PaintData unterschieden werden sollte, aber unbewusst/intuitiv hab ich das eh so gemacht. Und ja, wenn ich so drüber nachdenke, gebe ich dir recht, dass der genaue Typ im Interface eigentlich nicht viel verloren hat. Ich habs eben generisch ins Interface gepackt, weil ich dann zum Beispiel im Interface DogPaintData (welches Paintable extended) nicht die Methode nochmal (mit genauerem Returnwert) angeben muss. aber ich seh ja jetzt in meinem Code, dass ich überall Paintable<?> als Parameter habe, weil mir der spezielle Typ an diesen Stellen ganz egal ist. wichtig ist mir nur, dass die konnkreten Klassen wie Dog oder DogPaintData Dog-Objekte liefern (und das findest du ja auch ganz normal, wie du gesagt hast). aber ja, dann sollte ich das deswegen wohl nicht ins Paintable interface packen.
der grund, warum ich dies schon in eine abstrakte superklasse bzw. ein interface packen wollte, ist, dass ich in meinem konkreten beispiel ein paar methoden habe. also angenommen es gibt dann noch methoden wie getFriend() in Animal. jedes Animal hat einen Freund, wobei Hunde Hunde als Freunde haben (ist jetzt nur ein blödes beispiel...). und noch mehr solcher methoden. Dies führt dazu, dass ich bei jeder Klasse wie Dog, Cat, etc. sehr viele Methoden habe, die ich überschreiben muss, um den genauen returntyp anzugeben, bzw. es gibt sehr viele methoden, wo ich überprüfen muss, ob zb bei setFriend(Animal) der richtige Subtyp übergeben wird. solche dinge wollte ich umgehen, das war der einzige grund eigentlich.
allerdings sehe ich jetzt ja selbst, dass ich im Code dann oft zb "Animal<?,?,?>" stehen habe (vorher hatte ich nur den raw type verwendet, aber jetzt wo die Warnung auf on ist ...). Mein Denkfehler war, dass ich zB mit einem Animal<Dog, DogPaintData, DogSomething> arbeiten würde, aber das stimmt ja nicht. ich arbeite entweder mit einem Animal<?,?,?> (und dann sind mir typen auch egal) oder aber mit einem konkreten Tier wie Dog.
wenn ich Animal ungenerisch mache hats eben den einen Nachteil, dass ich nicht angeben kann, dass ein Dog bei setFriend(..) einen anderen Dog erwartet und kein Tier. Aber damit muss ich wohl leben.