Typisierte Collections
 
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern

Sollte eine Seite über TypisierteCollections nicht mit einer Erklärung derselben beginnen? -- HenningThielemann

Gestern hat sich ein sehr interessantes Gespräch ergeben. Wir sind zu keinem Konsens gekommen und so erhoffe ich mir hier neue Einsichten. Die These:

TypisierteCollections sollte ein grundlegender Baustein einer jeden Programmiersprache sein. Der Mangel dieses Features wertet die SpracheJava stark ab (z.B. im Gegensatz zur SpracheCpp).

Die Gegenthese lautete dagegen in etwa so:
TypisierteCollections sind nicht notwendig. Wer so unsauber programmiert, daß er sie nötig hätte, benötigt dringend grundlegendere programmiertechnische Nachhilfe.

Eine Internetrecherche zu diesem Thema brachte nichts brauchbares hervor.

Was habt ihr für Erfahrungen in diesem Bereich gesammelt? --bs


Meiner Meinung nach ist das Fehlen der typisierten Collections in Java ein Mangel. Wenn man in Java schon für Alles statische Typen benutzt, wieso nicht auch für Collections? (Übrigens, mit Generics ist dieses Manko für Collections auch in Java gelöst) Zu sagen, dass dies die SpracheJava stark abwertet, ist aber sehr übertrieben, vor allem im Vergleich zu SpracheCpp, wo man, wenn man will, jede Typüberpüfung wegcasten kann.

"...mit Generics ist dieses Manko für Collections auch in Java gelöst." Was sind Generics (in Java)? --bs

http://jcp.org/aboutJava/communityprocess/review/jsr014/
Soweit ich weiß geplant für JDK1.5; einen Compiler-Protoyp gibt's bereits unter http://developer.java.sun.com/developer/earlyAccess/adding_generics/

Aber: Es gibt Sprachen, wo Variablen überhaupt nicht typisiert werden (z.B. SprachePython oder SpracheSmalltalk) und ich sehe es nicht als Manko.

Klar, du nicht, du kriegst als Entwickler dein (versehentlich typfalsches) Programm ja auch anstandslos zusammengebaut. Deine User, bei denen das Programm nicht das tut was es soll, sehen das möglicherweise anders.

Volker, ist es nicht ein wenig anmaßend davon auszugehen, dass er sich nicht für das Wohl seiner Benutzer interessiert? Wäre es nicht konstruktiver anzunehmen, dass er eine andere Methode als statische Typisierung kennt, um Typfehler zu verhindern? Könnte es vielleicht sogar interesant sein, darüber zu diskutieren welche das sein könnten? Leicht verwundert über diesen Tonfall, IljaPreuß

Sachliche Argumente halte ich nicht für anmaßend. Ein solches (dass nämlich Typfehler bei nicht typisierten Sprachen zunächst erstmal durchrutschen, wohingegen sie es bei typisierten nicht tun) kann aus meinem Einwand recht gut herausgelesen werden, falls man nicht zu böswillig ist. -- vgl

Mhh, ok, vielleicht habe ich das ein wenig "zu scharf" interpretiert... ;-)

BTW, nicht-typisierte Sprachen ist in diesem Zusammenhang nicht ganz richtig - in dynamisch, aber streng typisierten Sprachen wie Smalltalk sind zwar Variablen nicht typisiert, Objekte aber schon (im Gegensatz z.B. zu C++, das zwar statisch, aber nicht streng typisiert ist). Techniken wie TestgetriebeneEntwicklung können in ersteren zu einer ähnlich hohen Typfehler-Abdeckung führen, wie statische Typisierung. -- ip

Das sagst du einfach so, ich halte es bestenfalls für Ansichtssache. Obwohl, in der von dir gewählten schwachen Form, dass die Technik es "könne", hast du natürlich recht, nur ist das einigermassen unerheblich, "Können" kann man mit Aufwand immer fast alles. -- vgl

Du hast sicherlich recht, dass das keine "gesicherte Erkenntnis", sondern nur eine auf Erfahrung beruhende Vermutung ist. Allerdings meinte ich schon, dass TestgetriebeneEntwicklung das wahrscheinlich mit einem ökonomischeren Aufwand erreicht, als zusätzliche statische Typisierung. Testen tue ich mein Softwaresystem ja sowieso - und ich frage mich, warum diese Tests ausgerechnet die von statisch typisierten Systemen abgefangenen Typfehler durchflutschen lassen sollten. Nach meiner Erfahrung spricht eben eher was für obige, als für letztere Vermutung (dass diese Fehler in signifikanter Anzahl durchflutschen)... -- ip

Ich, als Programmierer interessiere mich natürlich für die Meinung der Benutzer (Kunden) mehr als für die "Meinung" des Compilers. Die Benutzer geben mir ja Geld :-) Der Compiler hat mich schon oft im Stich gelassen, und (auch in C++) anstandslos völligen Schwachsinn kompiliert.

Das kann daran liegen, dass C++ keine besonders strenge Typkontrolle besitzt. Für mich ist es ein Grund, C++ zu meiden. -- HenningThielemann

Paradoxerweise ist das Typsystem in Java viel stärker als in C++. C++ hat zwar statische typisierung das sich mit Templates gut kombinieren lässt um statisch typisierete Collections leicht zu implementieren, aber eine Laufzeitüberprüfung der Typen gibt es in C++ nicht. So kann man in C++ auf jeden Bithaufen mit einem typisierten Pointer zugreifen. Seien es Casts:

  MeinTyp *pMeinTyp = (MeinTyp*) &Bithaufen;

oder Arrays von abgeleiteten Klassen:

  struct A {
    int i;
  };
  struct B : public A {
    double f;
  };
    
  void Test(A* pA, int iLen) {
    for (int i = 0; i < iLen; ++i) {
       cout << pA[i].i;
    }
  }
  
  B b[10];

  ...
  // hier knallt's, aber dem Compiler ist es egal
  Test(b, 10);

in Java hingegen kann man ein Objekt einer Variable mit einem nicht passendem Typ nicht zuweisen, weil Java ein starkes Typsystem besitzt. Zu behaupten, dass Java keine typsicheren Collections hat, ist aber nicht ganz richtig, denn man kann typsichere Collections programmieren. Aber wozu?

Wie auch immer, eine statische Typisierung unterstützt die Vermeidung von trivialen Tippfehlern, ersetzt aber kaum eine gute Teststrategie. Ich möchte meine Erfahrung nicht verallgemeinern, aber ich habe in Java wesentlich geringere Bughäufigkeit als in C++. (auch wenn das schwache Typsystem von C++ keine große Fehlerquelle ist). Ich sehe ein statisches Typsystem als eine Art Übrüfung der Korrektheit der gebauten Software. Leider wird die Korrektheit des Systems so nur nach formellen Kriterien überprüft - was dem Benutzer der Software kaum hilft. UnitTests hingegen sind Korrektheitsüberprüfungen die explizit so gebaut sind, dass sie die tatsächlich gewollte Funktionalität des Systems überprüfen. --gR

Ich glaube nicht, dass statische Typisierung eine Teststrategie ersetzen soll. Der Vorteil ist tatsächlich die ersparte Tipparbeit (mit einem Wrapper kann ja z.B. sauber typisiert werden). Ersparte Tipparbeit heißt aber auch weniger Redundanz. Vor diesem Hintergrund vermisse ich die statische Typisierung in Java sehr. --mj

Ich glaube, hier liegt ein Missverständnis vor: Java *ist* statisch typisiert, und genau das führt zu der Notwendigkeit von Techniken wie Templates, um Tipparbeit zu ersparen. Dynamisch typisierte Sprachen, in denen Typen erst zur Laufzeit anhand von Methoden-Signaturen überprüft werden, ersparen einem den ganzen Aufwand des Castens etc. -- ip

Aber in Java gibt es statische und sogar starke Typisierung der Variablen. Nur gibt es (ohne Generics), keinen einfachen Weg, Collections typsicher zu machen und deswegen sieht man oft Code wie diesen:

  ((MeineKlasse)i.next()).meineMethode();

Dieser Code ist typsicher, denn zur Laufzeit wird der Typ des Eintrages in der Collection überprüft, aber, und da hast Du recht, er ist redundant, weil man sich den hässlichen Cast nicht sparen kann. Wieso kann man nicht auf solche Casts einfach verzichten, und einfach sowas schreiben?

  Object o = i.next();
  o.meineMethode(); // würde fehlschlagen, wenn o keine Methode mit dem Namen meineMethode besitzt

Was bringt es überhaupt, Variablen durch einen Typ einzuschränken? In C++ ist es nötig, denn der Compiler wandelt sowieso alles in PointerAritmetik? um, aber was bringt es in Java? Werden dadurch Bugs weniger? Wird dadurch das Programmieren schneller? Oder das Kompilieren? Oder die Performance?

In Java bringt es nichts, weil das Typsystem monomorph ist. Generell, also mit einem ausdrucksmächtigeren Typsystem, bringt es den Gewinn, dass dein Compiler dich schon warnen kann, wenn du versehentlich den falschen Wert in die Variable hineinsteckst, nicht erst wenn du ihn herausholst.

Aber OK, nehmen wir an, statische Typisierung bringt Vorteile. Wieso müssen dann die Typen in Java nur die Klassen und Interfaces sein? Wieso kann ich nicht einen Typ anders beschreiben? Wieso kann ich nicht sowas schreiben?

   virtual void print(A output, B data) {      
      // So könnte man Typen beschreiben, aber eigentlich kann
      // es der Compiler *hier* auch aus dem Code ableiten.
      // Bei einer abstrakten Methode kann es der Compiler nicht
      // aus dem Code ableiten, da könnte sowas nützlich sein.
      types A, B {
        A {
         print(B);
        }
      }
      output.print(data);
   }

Der Compiler könnte doch selbst während der Kompilierung überprüfen, ob die Klasse von output eine Methode print mit einem Argument, der zum Typ von data zusammenpasst, besitzt. C++ kann was ähnliches mit Templates, in Java wäre sowas möglich, ohne dabei den Code wie in C++ aufzublähen. Tipparbeit würde es bestimmt ersparen. Aber Vorteile einer statischen Typisierung kann ich immernoch nicht so richtig erkennen. --gR

Statische Typisierung macht Code komplexer

Naja, StatischeTypisierung? hat schon den Vorteil, dass bestimmte Fehler eben schon zur Kompilierzeit gefunden werden, statt erst zur Laufzeit. Was allerdings häufig übersehen wird: sie hat auch Kosten, im Wesentlichen in Form von aufgeblähtem, komplexerem Code. Im Zusammenhang mit modernen TestStrategien? denke ich, dass die Kosten für die meisten Projekte (die nicht extrem hohe Sicherheitserfordernisse besitzen) einfach den Nutzen übersteigen. Als Astronaut würde ich mein Leben allerdings lieber einem statisch typisiert entwickelten Software-System anvertrauen... ;-) -- ip

Inwiefern macht StatischeTypisierung? den Code komplexer ?

Sie koppelt polymorphes Verhalten an Vererbungshierarchien und zwingt mich damit in vielen Fällen, abstrakte Klassen oder Interfaces allein für diesen Zweck einzuführen. In Fällen, in denen ich nicht in der Lage bin, diese Hierarchie herzustellen, verhindert sie sogar den Einsatz von Polymorphie und reduziert damit meine Fähigkeit, EinmalUndNurEinmal (und damit EinfachesDesign) konsequent durchzuhalten.

Blödsinn. Tut doch nicht immer so, als sei das Typsystem von Java das einzige existierende. Ja, in Java werden polymorphes Verhalten und Vererbung gekoppelt. Und nur dort.

Ein einfaches reales Beispiel:

closeIfNotNull(closeable) {
  if (closeable != null) {
    closeable.close();
  }
}

ist in einer dynamisch typisierten Sprache in der Lage, sämtliche Objekte zu behandeln, die eine close-Methode besitzen. Da es in der SpracheJava kein Closeable interface gibt (und ich jenes auch nicht nachträglich einführen kann), muss ich für Reader, Writer, InputStreams?, OutputStreams?, Sockets usw. usf. jeweils eine eigene Methode schreiben.

(Ich möchte anmerken, dass ich mich hier auf die StatischeTypisierung? beziehe, wie sie in der SpracheJava und SpracheCpp umgesetzt ist - wenn jemand von einer Implementierung weiß, die diese Probleme nicht aufweist, würde mich das sehr interessieren!)

Da hast du recht, das ist unschön. Allerdings zählt für mich das Argument nicht. Dein closealbe Problem ist statisch typisiert ja lösbar, nur nicht mit Sun's Implemtierung.

Natürlich hätte ich das Problem nicht, wenn Sun an ein Closeable interface gedacht hätte. Ich denke jedoch, dass es unrealistisch zu erwarten, ja gar unmöglich ist, alle solche Benutzungswünsche vorherzusehen und entsprechende Interfaces einzuführen - letztendlich würde es wohl darauf hinauslaufen, nahezu für jede Methode ein eigenes Interface einzuführen. Deshalb sehe ich das durchaus als allgemeines Problem von statisch typisierten Systemen.

Wenn du mit der dynamischen Typisierung den Sun Code in einer nicht vorhergesehen Weise einsetzen möchtest, ist das ... zumindest ungetestet ...

Wieso? Ich schreibe doch die Tests, die untersuchen ob das auch funktioniert, was ich da probiere...???

Aus dem Bauch heraus, hab ich Bedenken, dass andere diesen Mechanismus in der Folgezeit ungetestet benutzen, ist aber ein schwaches Argument, wie ich zugeben muss :-) -- mj

"(Ich möchte anmerken, ... wie sie in der SpracheJava und SpracheCpp umgesetzt ist ...)": Wieso wird in diesem Nachsatz auf C++ abgehoben, das zuvor gegebene Beispiel läßt sich in C++ doch problemlos so machen? -- vgl

Statische Typisierung verringert den Testaufwand

Durch statische Einschränkungen auf bestimmte Typen verkleinert sich der Parameterraum und dadurch der Testaufwand.

Ich bin für jedes Quäntchen statischer Überprüfung dankbar ... da ich dann weniger Tests brauche ...

Kannst Du ein Beispiel für einen Test geben, der in einem dymisch typisierten System gebraucht wird, in einem statisch typisierten aber nicht?

Nicht konkret natürlich ... du kannst immer behaupten, diesen speziellen Fall müsste man sowiso nicht testen :-)

Dann stimmt's ja vielleicht sogar? ;-p

Theoretisch aber ist dein Parameterraum deutlich groesser und heterogener -- also mehr Testaufwand.

Praktisch weiß ich doch aber ziemlich genau, welche meiner Objekte mit welchen anderen kollaborieren und muss diese Kollaborationen sowieso testen - also der gleiche Testaufwand?

Dann eben Praktisch:
int add(int a, int b) {
  return a + b;
}
Typisiert teste ich hier zwei oder drei int zahlenpaare. Was testest du hier ohne statische Typisierung ?
Und was machst Du, wenn Du zwei Floats oder zwei String addieren möchtest?

Ich sehe nicht, warum ich bei dynamischer Typisierung hier mehr testen sollte - wird die Methode mit ungeeigneten Typen verwendet, so sollte das von den Tests aufgedeckt werden, die diese Verwendung motiviert haben. -- ip

Na jeweils eine Methode für Floats und Strings, welches Verhalten zeigt die Methode, wenn ich ein Apfel und ein Birne Objekt addiere ?

Genau das Verhalten, das ich erwarte: wenn die Addition zwischen Äpfeln und Birnen definiert ist, erhalte ich das Ergebnis selbiger, ansonsten gibt es einen Laufzeit-Fehler.

Wenn Du aber jeweils eine Methode für die Addition von Strings und von Floats und von Äpfeln und von Birnen und von deren Kominationen implementierst, dann musst Du jede Methode erneut testen. Das widerspricht aber der Annahme, dass statische Typisierung die Testkomplexität verringert.

Richtig ich brauche Test für drei Methoden, die Methoden sind aber jede für sich einfach.

Sie sind nicht nur einfach, sie sind sogar (bis auf die Typangaben) identisch...

Im dynamisch typisierten Fall muss ich dafür sorgen dass für jede denkbare Kombination von Typen ein semantisch sinnvolles Ergebnis herauskommt und genau dies muss ich auch testen.

Nein, Du musst nur testen, dass die Methode funktioniert, wenn sie richtig benutzt wird - mit Typen, die "+" in einer für die Methode sinnvollen Art und Weise definieren. *Ob* sie richtig benutzt wird, wird an anderer Stelle sowieso getestet.

Zudem habe ich eine zusätzliche Quelle für RuntimeErrors?, die evtl. sinnvoll vom umgebenden Code behandelt werden müssen, was natürlich auch entsprechende Tests nach sich zieht.

Ich habe noch nie meinen Code so geschrieben, dass er mit ClassCastExceptions? umgehen kann. Ich sehe nicht, warum ich in einer dynamisch typisierten Sprache damit anfangen sollte...

Trotzdem glaube ich, dass statische Typisierung sehr hohen Wert in schwach typisierten Sprachen hat. Also in Sprachen wie C, C++, Pascal usw. wo man zur Laufzeit auf jeden Speicherplatz typisiert zugreifen kann. Da ist es extrem wichtig, dass z.B. die Methode add(int, int) die zwei z.B. 2 byte große Integer erwartet, nicht plötzlich mit zwei 4 byte großen String-Pointern aufgerufen wird.

Das ist wohl richtig - wobei mich in C aber niemand davon abhält, dass dennoch durch explizites Casten zu tun, trotz statischer Typisierung...

Test machen ein Projekt komplexer (zumindest ein bischen)

auch Tests machen ein Projekt komplex, das merkst du, wenn du mal eine Schnittstelle änderst :-)

Mhh, wenn von einer Schnittstellenänderung viele Tests betroffen sind, würde ich das als CodeGeruch empfinden. Außerdem würde ich vermuten (sorry Volker ;), dass die Tests in einem dynamisch typisierten System zumindest gegenüber gewissen Änderungen im Typsystem weniger anfällig sind.

Requests are allways changing ... riecht dann der Kunde oder der Code :-) ?

Über Deinen Kunden kann ich nichts sagen - aber wenn Änderungen an einer Stelle sich auf weite Teile des Systems auswirken, riecht der Code wohl mit Sicherheit, oder?

Ich glaub wir schreiben da anneinander vorbei. Ich spreche von einer Schnittstelle und den zu dieser Schnittstelle gehörenden Tests. Änderungen, die gesamte Software umwerfen, meine ich nicht damit.

Wenn Änderungen an Schnittstellen große Änderungen an Tests hervorrufen, habe ich bisher immer festgestellt, dass ich meine Tests nicht genügend refaktorisiert hatte. Wenn Du auch in Deinen Tests EinmalUndNurEinmal berücksichtigst, sollte das nämlich eigentlich nicht passieren... -- ip

Na hör mal, wenn ich die add Methode aus meinem obigem Beispiel um einen Parameter erweitere, muss ich das bei allen add Aufrufen ebenso tun. Dafür gibt's Tools, ich weiss, aber ohne Tests brauch ich nicht mal Tools (höchstens einen Psychotherapeuten, der mich tröstet, wenn mein Code dann mal nicht mehr funktioniert :-) ).

Wenn Du Dir eine "assertAdd"-Methode extrahiert hast, musst Du das in den Tests möglicherweise an genau einer Stelle tun.

Und das Refactoring Tool liefert mir auch gleich eine jeweils sinnvolle Belegung für den zusätzlichen Parameter, oder ;-) ?

Nach meiner Erfahrung hat bereits existierender Client-Code meist das Bedürfnis, überall genau den gleichen Wert für den neuen Parameter zu übergeben. Gute RefactoringBrowser bieten tatsächlich die Möglichkeit, diesen DefaultWert automatisch einfügen zu lassen, ja. Eine Alternative, die selbst ohne Tool funktioniert, ist häufig das Überladen der Methode mit dem zusätzlichen Parameter. Die alte Methode kann dann einfach die neuere mit dem zusätzlichen DefaultWert aufrufen.

Ausserdem kennst du ja meine Meinung v. XP in Zusammenhang mit Schnittstellenstabilität ...

Und Du meine... ;-)

Dynamisch Testen gegen statisches Validieren

Wenn ich dein Komplexitätsargument etwas auf die Spitze treiben darf: "Do it" ist die kürzeste statische Methode, mit der ich meinen Computer zu einer Aktion bewegen kann ... die genaue Spec. kann ja dann dynamisch geliefert werden ;-) Allerdings will ich soviel Dynamik nicht wirklich ...

Ich verstehe ehrlich gesagt den Zusammenhang mit dynamischer Typisierung hier nicht - nach meiner Einschätzung kann das Verhalten von dynamisch typisierten Systemen wohl genauso eng spezifiziert werden, wie bei statisch typisierten. Könntest Du das bitte näher erläutern ?

Ich wollte nur sagen, je mehr Dynamik im Spiel ist umso mehr Komplexität wird drum herum benötigt.

Sehe ich alles andere als genauso - einfache Dinge können genauso gut, häufig sogar besser, hohe Dynamik besitzen.

Den einfachsten Code schreib ich in möglichst engen statischen Grenzen und wenn's diese Grenzen gibt, möchte ich die auch artikulieren können! -- mj

Nach meiner Einschätzung korrellieren die Grenzen eines statischen Typsystems nicht immer besonders gut mit den angestrebten Grenzen eines Systems. Das wird z.B. immer dann offensichtlich, wenn ein DownCast? oder andere "Umschiffung des Typsystems" notwendig wird. YMMV

Okay, allerdings ist es ein riesen Vorteil, wenn der Compiler schon den Fehle erkennen kann.

Interessanterweise besitzen dynamisch typisierte Sprachen eine Compilierzeit von praktisch Null, so dass hier die Tests bereits fehlschlagen, wenn Du in einer statisch typisierten Sprache noch kompilierst... Hast Du schonmal einen Blick auf http://www.ensu.net/ geworfen?

Sorry, ich nix flash ... Aber findest du wirklich das Tests einen Ersatz für eine statische Typüberprüfung sein sollten ?

Ich habe die begründete Vermutung, dass die umfangreichen Tests, die ich bei TestgetriebenerEntwicklung sowieso schreibe, für die meisten Systeme ausreichende Typüberprüfung leisten, ja. -- ip

Ich glaube nicht daran ... vielleicht kannst du mich irgendwann mal bei nem Bier von deiner Meinung überzeugen :-)

Zumindest sehe ich in meinem Umfeld keine Möglichkeit sinnvoll auf statische Typisierung zu verzichten. Hast du schon konkret Mehrpersonen-Projekt- & Wartungsarbeit-Erfahrung mit dynamisch typisiert gesammelt ?

Nein, leider nicht. Ich weiß aber von einem Smalltalk-Projekt und kann mal nachfragen, ob nicht jemand seine Erfahrung mit uns teilen möchte...

Oh, ein Link *klick* ... "Play the Ensu Demo", Play klingt gut *klick* ... "ENSU DEMO", hm nix passiert ... Rahmenquelltext anzeigen mit Netscape ... finales Table-Tag fehlt ... -- vgl

Schade, die Demo ist nämlich wirklich interessant. Ich habe Ihnen 'ne Email geschickt, mal sehen ob sie's hinkriegen... Update: wird wohl noch eine Weile dauern: "I'll probably remember to mention it next time we do some work on the site. Which may not be soon as we are not marketing this stuff." -- ip

Es geht hier um Test <-> Validierung. Um mehr Fehlerklassen statisch erkennen zu können, investiere ich gerne in die statische Typisierung. -- mj

Ich habe schon in C++ geschriebene Programme abstürzen sehen :-)

Blau kommt von MichaelJerger
siehe auch WardsWiki:StaticTypeSafety, WardsWiki:DuckTyping
KategorieDiskussion
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 15. Dezember 2004 9:54 (diff))
Suchbegriff: gesucht wird
im Titel
im Text