# Generics: spricht etwas dagegen raw types zu verwenden?



## -frank (17. Aug 2007)

ich bin nach einem eclipse update nochmal die compiler-error/warnings-einstellungen durchgegangen und frage mich nun, ob ich bei verwendung von raw types ein warning ausgegen lassen soll oder nicht. also genauer gesagt hatte ich ich es bisher nicht und weiß nun nicht, ob es besser ist, wenn ich meinen code entsprechend ändere. prinzipiell ist es ja kein problem ein "List" zu einem "List<Object>" zu machen (oder List<?>), andererseits stelle ich mir die sinnfrage: wenn ich ein "List" sehe, dann denke ich mir das <Object> ja dazu bzw weiß ich ja, dass die Objekte in der Liste nicht näher definiert sind. IMO bringts also nix, das <Object> anzugeben.
kann aber gut sein, dass ich hier auf etwas wichtiges vergesse, weshalb ich hier mal nachfragen wollte, ob ihr raw types noch verwendet bzw. was dafür/dagegen spricht.


----------



## byte (17. Aug 2007)

Ich sehs so: Bei alten Projekten kann mans auch lassen, wenn einem das Refactoring sonst zu viel wird. Bei neuen Projekten sollte man direkt Generics richtig einsetzen. Wer das nicht tut, macht sich langfristig nur Mehrarbeit.


----------



## -frank (17. Aug 2007)

```
public static void print(Object[] array) {..}
        if (array != null) {
            print(Arrays.asList(array));
        } else {
            print((List) null);
        }
    }

    public static void print(List list) {..}
    }
```

in diesem beispiel hätte ich zb zwei methoden die eine liste bzw. ein array schön formatiert ausgeben sollen. weil der methoden name überladen ist (worüber man natürlich streiten kann, aber für mein beispiel muss es so sein  ), müsste ich dann sogar den cast in

```
print((List) null);
```
parametrisieren, was aus meiner sicht vollkommen sinnlos ist.


----------



## Beni (17. Aug 2007)

Du denkst dir "<Object>" dazu, ich würde zu "<?>" tendieren. Und schon haben wir ein Problem weil jemand ein <> nicht geschrieben hat :wink:

Und was soll erst der Compiler sagen? Er benutzt die Generics um Fehler zu finden, im erraten was ein Code meint, ist er nicht so gut...


----------



## SlaterB (17. Aug 2007)

der Sinn davon ist ja auch nicht, überall List<Object> zu schreiben, sondern List<TypDesInhalts>!

wenn du nicht gerade eine superallgemeine Anwendung hast,
wäre die Warning bei List<Object> auch gar nicht so dumm

bei Listen hätte ich persönlich kein Problem mit der Warning,
aber insbesondere bei Comparable wäre mir das zu nervig

und auch bei eigenen Klassen mit generischen Parameter,
die auch als Basisklasse mit dem bekannten Basis-Generic-Tyy Verwendung finden

siehe auch
http://www.java-forum.org/de/viewtopic.php?t=53319
ab Mitte zweiter Seite


----------



## -frank (17. Aug 2007)

Beni hat gesagt.:
			
		

> Du denkst dir "<Object>" dazu, ich würde zu "<?>" tendieren.



naja, bevor es Generics gab, konnte man alle Objekte in eine Liste schreiben. dasselbe verhalten erzielt man jetzt mit List<Object>. in eine List<?> kann ich hingegen nix schreiben, weshalb ich mir bei List ein <Object> dazudenke.
aber klar, wenn ich einen Parameter List<Object> habe und eine Liste übergeben will, dann ists wieder umgekehrt und nur List<?> akzeptiert alles, versteh also schon was du meinst. 

auf jeden fall mal danke an alle. da es mir ja nicht um mich persönlich geht, sondern darum, wie mein code auf andere wirkt, reicht es mir schon zu wissen, dass einige hier lieber die generics vorfinden. und die unterscheidung zwischen List<?> und List<Object> ist ja ne zusätzliche info, also sicher nix schlechtes.


----------



## -frank (17. Aug 2007)

SlaterB hat gesagt.:
			
		

> wenn du nicht gerade eine superallgemeine Anwendung hast,
> wäre die Warning bei List<Object> auch gar nicht so dumm



naja, ich habe einige GUI klassen/methoden, welche mit Listen arbeiten und zb nur die toString() methode brauchen, um text in zellen von tabellen zu setzen oder menuitems zu generieren. da brauche ich nicht nur List<?>, sondern auch List<Object>.
ansonsten aber äußerst selten.


----------



## -frank (17. Aug 2007)

byto hat gesagt.:
			
		

> Ich sehs so: Bei alten Projekten kann mans auch lassen, wenn einem das Refactoring sonst zu viel wird. Bei neuen Projekten sollte man direkt Generics richtig einsetzen. Wer das nicht tut, macht sich langfristig nur Mehrarbeit.



meinst du mehrarbeit, weil ich es früher oder später dann doch ändern würde oder weil es aufgrund der nichtverwendung von generics zu höherem aufwand kommt? wenn letzteres, warum? bzw. kannst du mir ein beispiel sagen? (ich werde generics einsetzen, weils mir wie gesagt schon reicht, dass es etliche leute hier wollen. aber mich persönlich störts wie gesagt nicht, wenn die <> fehlen (bei manchen methoden). speziell auch wenn mit Class-Objekten gearbeitet wird)


----------



## Tellerrand (17. Aug 2007)

-frank hat gesagt.:
			
		

> meinst du mehrarbeit, weil ich es früher oder später dann doch ändern würde oder weil es aufgrund der nichtverwendung von generics zu höherem aufwand kommt? wenn letzteres, warum?


Ohne Generics muss man die Casts selber schreiben und bei jedem Container drauf achten welchen Typ man nun in diesem verwaltet.

Generics sind imo nur eine Möglichkeit sich Schreibarbeit und die möglichen Flüchtigkeitsfehler zu sparen.
Und bei alten Projekten ist die Schreibarbeit kein Argument mehr


----------



## byte (17. Aug 2007)

Durch die Verwendung von Generics können eben viele Fehler schon zur Designzeit erkannt werden. Man spart insofern Zeit, weil durch die Verwendung somit viele Fehler gar nicht mehr auftreten können.
Wenn man natürlich so allgemeingültigen Code schreibt, dass man nur mit Objects hantiert, dann ist der Nutzen natürlich nicht so groß. Natürlich sollte man dann aber aus Konsistenzgründen auch <Object> schreiben.

Ich muss dazu sagen, dass ich mir bisher noch keine Gedanken darüber gemacht hab, weil ich quasi nie nur mit Objects hantiere, sondern eigentlich immer eigene Typen definiere. Du kannst ja mal drauf achten, wie häufig Du Deine Objects in andere Typen casten musst.


----------



## byte (17. Aug 2007)

Tellerrand hat gesagt.:
			
		

> Generics sind imo nur eine Möglichkeit sich Schreibarbeit und die möglichen Flüchtigkeitsfehler zu sparen.
> Und bei alten Projekten ist die Schreibarbeit kein Argument mehr



Die "Flüchtigkeitsfehler" sind gar nicht mehr so flüchtig, wenn Du mit einem größeren Entwicklerteam an einem Projekt arbeitest. Gerade über die Schnittstellen der Module hinaus können dadurch viele Exceptions im Vorhinein ausgeschlossen werden.


----------



## SlaterB (17. Aug 2007)

es geht hier doch nur um die Raw-Types-Warning, nicht um Generics an sich..,
wenn man List mit Usern hat, dann wird auch jeder List<User> schreiben,
das ist klar und ausgeklammert

es geht nur um die übriggebliebenen allgemeinen List- und sonstige Raw-Type-Anwendungen..


----------



## bygones (17. Aug 2007)

dank den collection verkommen die generics leider immer mehr bei den programmieren als nice to have... weniger casts und somit weniger schreibarbeit.

Dass sie mehr bieten uebersehen die meisten....


naja zum thema

muessen musst du nichts... wenn du keine lust hast und es einen zu grossen aufwand darstellt mach es nicht. Dank des legacy princips funktioniert der alte code ja auch noch...


----------



## Tellerrand (17. Aug 2007)

Okay 
Alte Anwendungen muss man ja nicht umformen, aber bei neuen würde ich schon das <Object> setzen.
Ich verfechte da 2 Grundsätze:
a) Man sollte möglichst nie Compiler Warnings ignorieren. Führt nur zu schlechten Angewohnheiten.
b) Wenn man <Object> schreibt meint man das auch und hat nicht nur die Typangabe vergessen.

Beispiel:

```
public LinkedList test() {
		LinkedList<Integer> l = new LinkedList<Integer>();
		return l;
	}
```
Erzeugt bei mir nur eine Warnung, ignorieren würde ich diese nicht.




> Dass sie mehr bieten uebersehen die meisten....


Dann sage mir was sie sonst bringen außer "weniger Schreib-/Denkarbeit" und auch weniger "Fehlerquellen".


----------



## SlaterB (17. Aug 2007)

diese Art der Warnung ist z.B. sinnvoll, die habe ich auch immer eingeschaltet,
wenn ich sie sehe, dann ist das zu 95% so einfach zu korrigieren wie in dem Beispiel und zu 5% kommt ein suppressWarning hin,
weil z.B. eine Hibernate-Ergibnis-Liste initial auf den richtigen Typ gemappt wird

aber bei Raw-Type-Warnings ist die Relation eben nicht 95%, sondern nahe 0%, 
wenn ich eine Klasse ohne Typ verwende, dann meine ich das auch so,
da kann es einfach zu keine Fehler kommen


----------



## Tellerrand (17. Aug 2007)

> aber bei Raw-Type-Warnings ist die Relation eben nicht 95%, sondern nahe 0%,
> wenn ich eine Klasse ohne Typ verwende, dann meine ich das auch so,
> da kann es einfach zu keine Fehler kommen.



Mein Beispiel erzeugt bei mir nur ein Raw-Type-Warning.
Und ich kann sogar test().add("irgendwas") aufrufen ohne etwas anderes zu bekommen.
Ist das bei dir anderst, oder hab ich jetzt was verpeilt?


----------



## SlaterB (17. Aug 2007)

stimmt, hab nicht so genau geschaut ,
aber ist dann umso mehr ein gutes Beispiel:

sofern die Operation so für sich steht, gibts keine Warning (nur das sinnlose raw-type), kein Problem, niemand wird verletzt,

wenn ich jetzt irgendwo
List list = test();
if (list == null) {
}

usw. schreibe, gibts immer noch keine Warning, warum auch?

aber wenn ich nun
List<Integer> list = test(); 
schreibe, das wäre ja halbwegs gefährlich,

schwupps kommt 'unchecked conversion' oder wie das heißt,
ich werde gewarnt und kann die Operation dann korrieren, alles paletti

wozu braucht man die raw-type-Warning?

in diesem Beispiel wäre sie vielleicht sinnvoll, wenn man eine Library schreibt, mit Operationen, die intern nie genutzt werden..


----------



## Tellerrand (17. Aug 2007)

Ich finde das Beispiel auch gut,
kann damit einen String (Oder irgendetwas anderes) in eine LinkedList<Integer> stecken und das einzige was ich bekomme ist eine Raw Type Warnung.  :shock: 

Natürlich ist so ein Fehler recht selten, aber man weiß von außen nicht, ob diese LinkedList nicht doch eine LinkedList<Irgendwas> ist.
Wenn ich eine LinkedList<Object> bekomme kann ich mir hingegen sicher sein. 


EDIT: Hmm, ich schau nochmal ob ich da wirklich nciht irgendeine andere Warning bekomme  

Also ich bekomme doch die unchecked Warning, aber *immer* wenn ich versuche irgendetwas einem Raw Type hinzuzufügen.
Ergo müsste man diese Warnung ebenso ignorieren, um mit einem Raw Typ zu arbeiten.
... da bleib ich doch lieber bei <Object>


----------



## SlaterB (17. Aug 2007)

> aber man weiß von außen nicht, ob diese LinkedList nicht doch eine LinkedList<Irgendwas> ist

? 
habe ich doch geschrieben: wenn man meint, dass es List<Integer> sein soll, also

List<Integer> list = test(); 
schreibt, dann kommt  'unchecked conversion'


----------



## Tellerrand (17. Aug 2007)

> List<Integer> list = test();
> schreibt, dann kommt 'unchecked conversion'



Anderes Beispiel:


```
import java.util.LinkedList;

public class TestMain {
	
        private static LinkedList<Integer> list = new LinkedList<Integer>(); 
         /* private static LinkedList list = new LinkedList(); */

    public static LinkedList test() { 
           return list; 
    }
	
	
	public static void main(String[] args) {
		
		
		test().add("asd");
	}
}
```

Ob ich nun RawTyp verwende oder den Integer Typ, in beiden Fällen die gleiche Warnung.


----------



## SlaterB (17. Aug 2007)

ja, das macht Ärger, das akzeptiere ich


----------



## -frank (18. Aug 2007)

also dies verwundert mich jetzt wieder:


```
public class Main2 {

    private static class A<T extends A<T>> {}

    private static class B extends A[B] {}

    public static void main(String[] args) {
        B b = new B();
        A a = b; // warning (raw type)
        A<?> a2 = b;
        A[B] a3 = b;

        System.out.println(b == a); // funkt
        System.out.println(b == a2); // compilation error: incomparable types
        System.out.println(b == a3); // funkt
    }
}
```

díes ist ziemlich nervig. warum kann simpler == check nicht auch mit A<?> gemacht werden? dies zwingt mich in diesem fall ja sogar dazu, den raw type zu verwenden und dann ein suppresswarnings zu setzen.

(falls ihr da keinen compiler error bekommt, siehe anderes posting: http://www.java-forum.org/de/viewtopic.php?t=54486)


----------



## Tellerrand (18. Aug 2007)

Ganz ehrlich,
das sind Konstrukte bei denen sich meine Nackenhaare aufstellen.
Das Design sträubt sich gegen OOP und am Ende wird sowas in 99% der Fälle Ärger bereiten.

Da bist du mit deiner Frage jetzt auch tief im Thema Generics drin, mit dem Innenleben der Wildcards kenn ich mich auch nicht sonderlich gut aus.
Dürfte afaik daran liegen, dass die statische Typinformation vollkommen verschieden ist, der Typ B ist dem Compiler bekannt, der Typ A<?> ist hingegen zur Compilezeit schon nicht bekannt. Daher resultiert das Problem zwei Referenzen unterschiedlichen Types, es wäre ja möglich, dass A<?> von einem gaaanz anderem Typ ist.

Näheres findet man vielleicht hier:
http://www.angelikalanger.com/Articles/JavaMagazin/Generics/GenericsPart2.html


EDIT:
Probier mal ein A<? extends A> aus  :shock:


----------



## -frank (19. Aug 2007)

Tellerrand hat gesagt.:
			
		

> EDIT:
> Probier mal ein A<? extends A> aus  :shock:



macht keinen unterschied. sollte es IMO auch nicht machen, da der compiler ja A<?> sowieso zu A<? extends A<?>> machen kann.



> Ganz ehrlich,
> das sind Konstrukte bei denen sich meine Nackenhaare aufstellen.
> Das Design sträubt sich gegen OOP und am Ende wird sowas in 99% der Fälle Ärger bereiten.



und ich überlege auch, ob ichs wieder rausnehmen soll. ich finds ja auch schrecklich. aber auf der anderen seite erspare ich mir durch solche konstrukte sehr viel redundanten code, sehr viele casts, etc.
du sagst es sträubt sich gegen OOP und naja, kann dir da eigentlich nur zustimmen. aber IMO sträubt es sich doch auch gegen OOP, wenn ich code, den ich in einer superklasse implementieren könnte stattdessen in x anderen klassen stehen habe, nur um zum beispiel die methode zu überschreiben und einen genaueren typ zu returnieren. oder aber ich muss ständig instanceof checks machen bzw. casten und dokumentieren, dass die unterklasse einen spezielleren typ verlangt. diese dinge erspare ich mir, wenn ich typen über generics an die superklasse bzw. das interface weitergebe.

ich weiß einfach nicht bzw. kann mich nicht entscheiden, was besser ist.
edit: aber ich habe gerade eine idee, einen teil der probleme mit interfaces zu umgehen. mal sehen, ob das klappt...


----------



## Tellerrand (19. Aug 2007)

-frank hat gesagt.:
			
		

> macht keinen unterschied. sollte es IMO auch nicht machen, da der compiler ja A<?> sowieso zu A<? extends A<?>> machen kann.


Sollte es nicht, nein ... aber Compiler müssen ja nicht so intelligent sein, war nur eine reine Vermutung 




			
				-frank hat gesagt.:
			
		

> du sagst es sträubt sich gegen OOP und naja, kann dir da eigentlich nur zustimmen. aber IMO sträubt es sich doch auch gegen OOP, wenn ich code, den ich in einer superklasse implementieren könnte stattdessen in x anderen klassen stehen habe, nur um zum beispiel die methode zu überschreiben und einen genaueren typ zu returnieren. oder aber ich muss ständig instanceof checks machen bzw. casten und dokumentieren, dass die unterklasse einen spezielleren typ verlangt. diese dinge erspare ich mir, wenn ich typen über generics an die superklasse bzw. das interface weitergebe.



Normalerweise braucht man nur den allgemeinen Return Typ der Superklasse.
Das Code in mehreren Klassen neu implementiert werden muss kann auch an der fehlenden Mehrfachvererbung liegen und ist nicht immer ein Zeichen von schlechtem Design.
Das Ziel ist es doch (soweit ich es verfolgt habe) von jeglichen Printable Objekten sich PrintData liefern lassen zu können. Also z.B. eine Methode schreiben zu können, welche eine Liste aus Printable Objekten verarbeitet. Wenn jetzt Printable Objekte unterschiedliche "Arten" von PrintData liefern, kann man sich dies auch sparen, denn die Gemeinsamkeit, welche die Superklasse implementiert, existiert anscheinend nicht.

Andere Erklärung:
Wenn du es schaffst, dass deine verschiedenen Subklassen unterschiedliche Typen retunieren, dann frage ich mich immernoch an welcher Stelle das hilft. Eine Methode print(Printable p) müsste immernoch jedes Printable Objekt anderst behandeln.
Im Prinziep ist die Verwendung von instance of in 99% der Fälle schon auf schlechtes Design zurück zu führen, diese Verwendung nun mit Generics versuchen zu ersetzen ändert imo nicht viel am Design.

Dummerweise kann man dir auch kein alternatives Design vorschlagen ohne genau zu wissen was verlangt ist.
Im Extremfall ist das Design auch nicht verkehrt, sondern es ist einfach das Problem welches etwas haarig ist.


----------



## -frank (19. Aug 2007)

Tellerrand hat gesagt.:
			
		

> Wenn du es schaffst, dass deine verschiedenen Subklassen unterschiedliche Typen retunieren, dann frage ich mich immernoch an welcher Stelle das hilft. Eine Methode print(Printable p) müsste immernoch jedes Printable Objekt anderst behandeln.



diesbezüglich kann ich dir nicht folgen, da dies bei mir oft der fall ist und es für mich auch ganz normal ist.
ich meine, dass eine Klasse Dog ein Objekt DogPaintData liefert und eine Klasse Cat CatPaintData returniert, das ist doch nicht ungewöhnlich oder?? da hab ich dann eben unterschiedliche returnwerte in den subklassen.

und ob print(printable p) dann die objekte unterschiedlich behandeln muss, ist IMO einfach von der implementierung abhängig. zb könnte diese methode sehr wohl unterschiedlich behandeln, eine andere methode wie printInfo(Printable) aber nur daten über die größe, die anzahl der farben oder sowas auslesen und dafür die untertypen eventuell garnicht brauchen. in meinem fall ists so, dass ich oft allgemeinere methoden habe, die nur den supertyp PaintData verlangen oder Animal oder sowas.

hast du das jetzt anders verstanden bzw. hab ich dich falsch verstanden oder meinst du wirklich, dass dies schon auf schlechtes design deutet? wenn ja, erklär mir bitte warum!


----------



## Tellerrand (19. Aug 2007)

Ich denke wir verstehen uns hier gegenseitig nicht, ich habe das auch etwas unglücklich formuliert.



> ich meine, dass eine Klasse Dog ein Objekt DogPaintData liefert und eine Klasse Cat CatPaintData returniert, das ist doch nicht ungewöhnlich oder?? da hab ich dann eben unterschiedliche returnwerte in den subklassen.


Das ist ganz normal, nur warum willst du dies in der Superklasse implementieren, darum ging es mir.



> und ob print(printable p) dann die objekte unterschiedlich behandeln muss, ist IMO einfach von der implementierung abhängig. zb könnte diese methode sehr wohl unterschiedlich behandeln, eine andere methode wie printInfo(Printable) aber nur daten über die größe, die anzahl der farben oder sowas auslesen und dafür die untertypen eventuell garnicht brauchen.


Meiner Meinung nach sollte eine Methode print(Printable p) *möglichst nie* Rücksicht darauf nehmen welcher genaue Subtyp nun übergeben wird, sondern wie du es auch selber schon geschrieben hast den allgemeinen Typ verwenden.
Genauso ist es meine Meinung, dass wenn man eine Liste, gefüllt mit Objekten unterschiedlichen Typs, hat und die Objekte dieser Liste dann je nach Typ unterschiedlich verarbeiten will, dann existiert ein Designfehler.



> meinst du wirklich, dass dies schon auf schlechtes design deutet? wenn ja, erklär mir bitte warum!


Ja, es deutet in meinen Augen auf ein schlechtes Design hin. Begründung ist die Alternative von vielen Casts, instance of, etc.
Genaueres sagen, wie z.B. "Ja das ist schlecht, weil..." oder "Ist ok, sehe da nichts falsches" kann ich nur wenn ich auch weiß was du genau versuchst zu implementieren und z.B. ein Klassendiagramm vorliegen habe.
Also bleibe ich dabei zu sagen "Das Design scheint mir _komisch_ zu sein"


----------



## -frank (20. Aug 2007)

@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.


----------



## Tellerrand (20. Aug 2007)

-frank hat gesagt.:
			
		

> 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.



Hier sehe ich auch das Problem am Design, du willst in der Superklasse eine Methode definieren die nicht für alle Subklassen gilt.
Im schlimmsten Fall definierst du später von Dog noch weitere Subklassen, z.B. BigDog und LittleDog und hängst an der selben Stelle ... wie macht man es möglich, dass LittleDog Objekte auch nur LittleDog Freunde haben dürfen.
Meiner Meinung nach ist die Methode setFriend also nicht für alle Subklassen von Animal gleicherweise gegeben, ich würde sie also auch nicht auf diese Weise in die Klasse Animal stecken.

Ein einfaches alternatives Design ist z.B. die Methoden:

```
boolean isPossibleFriend(Animal a)
// und
void setFriend(Animal a) throws IllegalFriendException
// (Oder halt irgendeine andere passende Exception)
```
in die Klasse Animal zu stecken.

Fehlt noch eine Art Vorgehensweise für die Prüfung von isPossibleFriend, aber wenn man diese hat wäre es imo schöner. Egal welches Tier ich habe und welches Tier ich als Freund setzen will, ich kann mit isPossibleFriend nachfragen obs passt und dann mit setFriend setzen, ... unabhängig von jeglichen Subklassen.


Nunja, das nur so als Anregung, das Problem ist wirklich etwas haarig und eine richtig elegante Lösung fällt mir da auf Anhieb auch nicht ein.  :?


----------



## -frank (20. Aug 2007)

Tellerrand hat gesagt.:
			
		

> -frank hat gesagt.:
> 
> 
> 
> ...



weiß nicht... ich will definieren, dass alle Tiere Freunde haben können aber eben nur Freunde vom selben Typ. wenn das eine abbildung der "real world" bzw. eben so gefordert ist, dann würde ich es nicht als schlechtes design ansehen bzw. wüsste ich nicht wirklich, wie ich es anders machen kann.

dein codebeispiel ist aber ein guter vorschlag IMO. ich werde im interface definieren, dass zb setFriend() eine exception werfen kann, falls der falsche typ übergeben wird. und sowas wie isPossibleFriend() macht absolut sinn! den check bzw. das setzen kann ich dann aber wohl trotzdem noch in einer abstrakten klasse machen. der abstrakten klasse kann ich dann auch ein paar generics geben um genauere return-werte zurückzugeben bzw. ein Class-Objekt, um den isPossibleCheck zu implementieren. in der abstrakten klasse stören mich diese dinge viel weniger, da sie - und ich hoffe ich irre mich da nicht (bin erst dabei es auszuprobieren) - wirklich nur bei der implementierung eines neuen Tieres relevant sind. Sämtliche Clientklassen haben nur ne Referenz auf das Interface Animal anstatt auf AbstractAnimal (und somit ist das Generic bzw. RawType-Problem keines mehr).
das einzige, was ich damit natürlich nicht erreiche, ist, den Typ von setFriend() genau anzugeben. ich hätte zwar die information dazu in AbstractAnimal, aber natürlich darf ich das Interface Animal nicht einschränken. daher muss ich eben wie in deinem vorschlag Animal akzeptieren und ggf. ne exception werfen. aber ich kann sehr wohl den Check in der zentralen (abstrakten) klasse machen und ich kann dort bzw. im interface auch die notwendige dokumentation platzieren. und die genaueren returnwerte kann ich ebenfalls returnieren. hab also trotzdem die redundanzen raus.
ich denke, dass es so ganz gut klappen sollte. aber muss es noch testen...


----------



## Tellerrand (20. Aug 2007)

-frank hat gesagt.:
			
		

> Tellerrand hat gesagt.:
> 
> 
> 
> ...



Vielleicht liegt ein Problem auch hier, für mich bestimmt die Aufgabenstellung(=Problem) das Design.
Wenn der Typ eines Tieres eine Information sein soll, dann würde ich gegebenenfalls diesen Typ (nur) als Attribut/Information nutzen.
Also ein enum AnimalTyp definieren und getAnimalTyp() als Methode in Animal implementieren.
Erst wenn das Problem Vererbung sinnvoll macht (Z.B. Unterschiedliche Tiere = unterschiedliche Methoden / Attribute) würde ich diese auch nutzen. Wobei die beiden Varianten sich nicht gegenseitig ausschliessen, man kann Vererbung nutzen, aber trotzdem noch ein Attribut AnimalTyp einfügen.

Die Typinformation von Java als Information im Programm selber zu nutzen ist manchmal fragwürdig.
Das endet öfters im Bereich Reflection, wenn man mit dieser Information komplexere Dinge machen will.
Oft wäre es dann bedeutend einfacher gewesen hätte man diese Information als Attribut, Variable angesehen.
(Was ist z.B. wenn du später ausgeben willst ob das Tier nun ein Vogel oder ein Hund ist?)

Nunja, das ist halt alles sehr vage, ich kann hier auch nur Anregungen liefern ein Kochrezept existiert ja nicht


----------



## -frank (20. Aug 2007)

Tellerrand hat gesagt.:
			
		

> > Nunja, das ist halt alles sehr vage, ich kann hier auch nur Anregungen liefern ein Kochrezept existiert ja nicht
> 
> 
> 
> ...


----------

