Braucht man Mehrfachvererbung



  • Helium schrieb:

    type Bar = { 
       methode : void -> void;
       weitereMethode : void -> void;
       undNocheine : void -> void;
    } 
    
    bar : Bar = Foo();
    

    Und warum genau klappt das ganze nicht mehr, wenn wir Bar erweitern?

    type Bar = {
      methode : void -> void;
      weitereMethode : void -> void;
      undNocheine : void -> void;
      hash : void -> int { return someHash(); }
    };
    
    var bar : Bar = Foo();
    

    Foo kann sich jetzt aussuchen ob es hash implementiert oder ob der default hash reicht.

    hash kann noch einen Schritt weiter gehen und auf methoden basieren die in Bar abstrakt sind:

    type Bar = {
      methode : void -> void;
      weitereMethode : void -> void;
      undNocheine : void -> void;
      call_all : void -> void { methode(); weitereMethode(); undNocheine(); }
    };
    

    Bar bleibt ein Typ, es ist keine vollstaendige Bauvorschrift. Es definiert lediglich Rahmenbedingungen. Es gibt lediglich einen weiteren Zustand:
    Bar verlangt, dass methode implementiert werden muss. Wenn methode fehlt, ist das Objekt nicht vom Typ Bar.
    call_all geht jetzt den Weg zu sagen: Alles was ein Bar ist, hat eine call_all methode. Wie diese methode implementiert ist, ist mir egal - aber ich kann fuer jedes Bar Objekt call_all ausfuehren.

    Das Problem hierbei ist, dass die Syntax sehr limitiert ist. Man koennte call_all ja auch als freie Funktion schreiben, nur ist C++ und Java zu dumm fuer soetwas, weil es kein dispatching kann.

    void call_all(Bar& b) {
      b.methode();
      b.weitereMethode();
      b.undNochEine();
    }
    

    Wenn wir nun vernuenftiges dispatching zur laufzeit haetten, koennten wir call_all fuer einen subtyp von Bar so schreiben:

    void call_all(Baz& b) {
      b.methode();
      b.weitereMethode();
      b.undNochEine();
      b.undEineVierte();
    }
    

    Und schon braeuchten wir die ganze diskussion nicht mehr fuehren 😉



  • Jetzt beschreibst du "Traits" (nicht C++-Traits, sondern was der unbekannte Rest der Programmiererwelt als Traits bezeichnet). Ich glaube das erste Paper kommt von Martin Odersky (?).
    Aber das sind definitv keine reinen Typinformationen mehr, mit deren Hilfe formal der Code auf Typfehler überprüft werden kann.



  • Helium schrieb:

    Jetzt beschreibst du "Traits" (nicht C++-Traits, sondern was der unbekannte Rest der Programmiererwelt als Traits bezeichnet). Ich glaube das erste Paper kommt von Martin Odersky (?).
    Aber das sind definitv keine reinen Typinformationen mehr, mit deren Hilfe formal der Code auf Typfehler überprüft werden kann.

    Das Problem ist nunmal diese Konzepte in die Syntax von Sprachen wie C++ und Java einzubauen.

    Ich waere sofort dafuer zu sagen, dass ein Typ lediglich definiert was alles da sein muss - und man Verhalten problemlos ueber freistehende Funktionen implementieren kann/soll.

    Nur da es das leider nicht spielt, muss man irgendwo einschnitte machen. Wie gesagt ein

    type Bar = { 
       methode : void -> void;
       weitereMethode : void -> void;
       undNocheine : void -> void;
    } 
    
    void call_all(Bar b) {
      b.methode();
      b.weitereMethode();
      b.undNochEine();
    }
    
    bar : Bar = Foo();
    

    waere mir am liebsten.

    Nur C++, Java und die meisten Sprachen bieten mir diese Moeglichkeit nicht. Was kann man also tun um das call_all dennoch zu bekommen?



  • Shade Of Mine schrieb:

    ich will nichts von implementierungsdetails wissen - ich will eine logische begründung haben wie du ein serializable implementieren willst. In Java ist das nicht ohne hacks möglich. Und sobald man hacken muss ist das design imho fehlerhaft. Und man kann nichtmal alles hacken - ich kann zB meine serialisierten daten nicht verschlüsseln oder mich anders serialisieren als java es will.

    Was brauchst du da für hacks?
    Musst doch nur

    private void writeObject(java.io.ObjectOutputStream out) throws IOException
    

    und

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
    

    implementieren und schon kannst du da z.B. verschlüsseln



  • wobei die eingebaute serialisierung in java ein ganz finsteres thema ist. das ist mir zu sehr quer gehackt und voller magie.

    mehrfachvererbung wurde aus java nicht entfernt, weil man sie unter keinen umständen benötigt. es gibt sicher fälle, in denen mehrfachvererbung für ein problem eine recht gute lösung darstellt. aber mehrfachvererbung kommt mit den kosten komplexeren codes. dokumentationen müssen in solchen systemen viel regeln, was durch abwesenheit von mehrfachvererbung schlicht nicht passieren kann.

    programmiersprachen haben sich immer weiter entwickelt, um dem entwickler werkzeuge an die hand zu geben, auch große projekte wartbar und durchschaubar zu halten. objektorientierung ist da ein ganz großes thema. es gibt durchaus fälle, in denen ein simples ansi-c programm mit ein paar globalen variablen und methoden ein beliebiges problem schnell, effizient und sogar elegant löst. aber sobald es komplexer wird, hat man liebend gern die möglichkeiten von OO zur verfügung.
    als java entwickelt wurde hatte man bereits die erfahrung gemacht, dass mehrfachvererbung stetige ursache von programmierfehlern ist. wiegt man das mit dem nutzen auf, liegt es nahe, dieses konzept schlicht nicht zu übernehmen. ebensowenig, wie java globale namensräume oder pointerarithmetik kennt. viele konzepte, die c/c++ entwickler liebgewonnen hatten, aber wie gesagt fehlerträchtig waren.
    wenn man sich darauf einlässt, kommt man damit auch wunderbar zurecht.

    wenn ich mir code anderer leute angucke sehe ich häuftig schlechten code. egal in welcher sprache. aber ich muss mir wesentlich seltener bei java code die hände über dem kopf zusammmenschlagen, als bei so manch vermurkstem c++ konstrukt. und das verdankt die sprache dem korsett, welches einem entwickler angelegt wird.



  • Shade Of Mine schrieb:

    Nur weil eine Klasse abstrakt ist, heisst es noch lange nicht dass sie nicht verhalten haben kann. Die Schnittstelle wird definiert und diese Schnittstelle darf ruhig verhalten haben.

    [...]

    Und wieder jemand der es nicht verstanden hat. Ich habe nie etwas gegen Schnittstellen gesagt - ich rede gegen die Java Definition einer Schnittstelle.

    Vielleicht erklärst du uns mal, was DU unter einer Schnittstelle verstehst? Offenbar nicht das, was die Allgemeinheit darunter versteht. Denn ein Java-Interface entspricht genau dem, was eine OO-Schnittstelle im Allgemeinen ausmacht.



  • Ich behaupte nicht, dass Java der Weisheit letzter Schluss in Sachen Objektorientierung ist (C++ noch viel weniger). Allerdings sehe ich die Probleme eher woander als im Schnittstellenkonzept.

    Andere z.B. dynamische Sprachen wie Python oder Ruby (oder auch das schon erwähnte Smalltalk) lösen dieses Problem anders, z.B. durch Mix-Ins, die sicherlich eleganter sind als Mehrfachvererbung. Aber es ist eben ein anderes Konzept; Typen sind hier irrelevant, Interface unbekannt.

    Shade Of Mine schrieb:

    Aber Java bemueht sich einfach zu sehr den Programmierer am schlechten Code schreiben zu hindern (was natuerlich nicht klappt) dass einige schoene Designs einfach nicht moeglich sind.

    Diese Trennung Interface/Klasse ist super fuer anfaenger, denn wie oft haben wir schon gesehen dass man eine Klasse Autos von Auto und std::vector ableiten will, anstatt einen std::vector<Auto> zu erstellen? Java verbietet soetwas by design. Super Sache ansich. Aber wenn man mal tiefer einsteigt, dann beengt einen Java.

    Das hört man leider ziemlich oft. Bedenke, dass Software meistens in Teams entwickelt wird und nicht
    immer alle Teammitglieder sind solche Cracks, dass sie für dich elegante „Expertenlösungen“ verstehen
    und warten können. Noch schlimmer, wenn der Experte irgendwann das Team verlässt und keiner
    mehr seine Hinterlassenschaften durchschaut. Am schlimmsten, wenn der Experte nur von sich glaubt,
    ein solcher zu sein und die Konsequenzen seiner Lösung nicht durchschaut. Die Erfinder von Java
    haben sich nicht ohne Grund dazu entschlossen, einige „Killer-Features“ nicht anzubieten. Siehe auch:

    thordk schrieb:

    wenn ich mir code anderer leute angucke sehe ich häuftig schlechten code. egal in welcher sprache. aber ich muss mir wesentlich seltener bei java code die hände über dem kopf zusammmenschlagen, als bei so manch vermurkstem c++ konstrukt. und das verdankt die sprache dem korsett, welches einem entwickler angelegt wird.

    Was euch so verwirrt ist, dass fuer mich ein interface mehr ist als das was Java als interface definiert.
    Wenn ich mir die 2 Links zu dem GoF Buch ansehe, dann stelle ich fest dass du entweder das Buch nicht verstanden hast, oder das was ich sage.

    Letzteres. Definitiv.

    Die Schnittstelle wird definiert und diese Schnittstelle darf ruhig verhalten haben.

    Implementierungsvererbung wie es dort so schoen heisst, ist die Implementierung eines Objektes mit Hilfe eines anderen.

    Ich sehe da nicht den kleinsten widerspruch zu dem was ich bisher gesagt habe. Ich will ja nicht einen vector mit hilfe von serializable implementieren - ich will dass vector die schnittstelle serializable definiert (und nebenbei will ich ein vernuenftiges default verhalten haben, dass ich ueberschreiben kann wenn noetig).

    Ich glaube unsere Interpretation der Aussagen im GoF-Buch unterscheiden sich ziemlich -- ums mal vorsichtig auszudrücken.

    Erklaer mir bitte einfach nur folgenden Punkt:

    Von einem OOD standpunkt aus, ist keins davon wirklich schoen.
    funktionieren tun sie alle, aber warum ist die kuerzeste und direkteste variante die schlechteste?

    Zu deinem serializable Vector Bespiel:
    Wie würdest du mit Mehrfachvererbung folgendes lösen: Du hast Vektoren, die sich in einen
    String serialisieren sollen, andere in XML oder in eine Binärform oder in eine Datenbank
    (nenn es meinetwegen nicht serializable sondern persistable).
    Hast du dann 1000 Vektor-Klassen wie StringSerializableVector, XMLSerializableVector, BinarySerializableVector, SqlSerializableVector, SqlOracleSerializableVector, SqlOracleVersion8SerializableVector, usw.?? Wenn du jetzt noch ein Set und eine Map
    mit den gleichen serializable-Funktionalitäten haben willst, dann multipliziert sich die Anzahl Klassen nochmal.



  • Shade Of Mine schrieb:

    Wie gesagt ein

    type Bar = { 
       methode : void -> void;
       weitereMethode : void -> void;
       undNocheine : void -> void;
    } 
    
    void call_all(Bar b) {
      b.methode();
      b.weitereMethode();
      b.undNochEine();
    }
    
    bar : Bar = Foo();
    

    waere mir am liebsten.

    Nur C++, Java und die meisten Sprachen bieten mir diese Moeglichkeit nicht. Was kann man also tun um das call_all dennoch zu bekommen?

    Was soll das bitte sein? Soll call_all(Bar b) eine Funktion im Typ Bar sein?
    Es ist doch nun mal so dass man zwei Dinge unterscheidet: Schnittstelle (also die Typinformation) und die Implementierung der Schnittstelle (also die konkrete Klasse). Java bietet dir halt die Möglichkeit, mit dem Java-Interface explizit nur den Typ zu spezifizieren. Klassen spezifizieren auch einen Typ, aber liefern auch direkt die Implementierung. Abstrakte Klassen sind eine Mischung aus reiner Schnittstellenspezifikation und Implementierung.

    Was genau fehlt Dir daran? Was genau findest Du schlecht? Was genau möchtest Du besser machen?



  • tfa schrieb:

    Zu deinem serializable Vector Bespiel:
    Wie würdest du mit Mehrfachvererbung folgendes lösen: Du hast Vektoren, die sich in einen
    String serialisieren sollen, andere in XML oder in eine Binärform oder in eine Datenbank
    (nenn es meinetwegen nicht serializable sondern persistable).
    Hast du dann 1000 Vektor-Klassen wie StringSerializableVector, XMLSerializableVector, BinarySerializableVector, SqlSerializableVector, SqlOracleSerializableVector, SqlOracleVersion8SerializableVector, usw.?? Wenn du jetzt noch ein Set und eine Map
    mit den gleichen serializable-Funktionalitäten haben willst, dann multipliziert sich die Anzahl Klassen nochmal.

    Selbstverständlich wäre die zu implementierende serialize-Methode unabhängig von der Art der Ein- bzw. Ausgabe. Daher reicht es, von Serializable abzuleiten.
    Sowas muss doch auch in Java machbar sein.



  • .filmor schrieb:

    Sowas muss doch auch in Java machbar sein.

    Ist es, wie schon mehrfach angesprochen. Nur eben nicht überImplementationsvererbung druch Mehrfachvererbung.



  • byto wech schrieb:

    Was genau fehlt Dir daran? Was genau findest Du schlecht? Was genau möchtest Du besser machen?

    Die unimplementierbarkeit von call_all().

    Es ist einfach nicht möglich das in Java zu machen.

    Der beste Ansatz wäre, freistehende Funktionen zu haben die das Verhalten definieren. Dazu müsste man aber weggehen von dem Klassenkonzept. Dann hätte man eine Menge Probleme nicht und diese ganze Diskussion hier wäre unnötig.

    Aber wir haben Klassen und müssen damit leben. Klassen sind furchtbar einnengend. Und Java Interfaces engen noch mehr ein. Die Idee einer reinen minimalen Schnittstelle ist ja nicht schlecht, nur in Sprachen wie Java und C++ nicht verwendbar.

    Man braucht sich nur Serializable ansehen. Es ist in Java nicht ohne Hack implementierbar. Das muss einem doch zu denken geben, oder?



  • tfa schrieb:

    .filmor schrieb:

    Sowas muss doch auch in Java machbar sein.

    Ist es, wie schon mehrfach angesprochen. Nur eben nicht überImplementationsvererbung druch Mehrfachvererbung.

    Hab ich dich nur falsch verstanden oder hast du es tatsächlich geschafft, eine Zeile eines aus zwei Zeilen bestehenden Posts ihres Kontextes zu berauben?

    Mir ging es darum, dass es auch in Java machbar sein muss, dass serialize unabhängig von der Art der Serialisierung (String, XML, was auch immer) nur einmal implementiert wird.



  • Braucht man Mehrfachvererbung?

    Definitiv Nein



  • Shade Of Mine schrieb:

    Man braucht sich nur Serializable ansehen. Es ist in Java nicht ohne Hack implementierbar. Das muss einem doch zu denken geben, oder?

    Das behauptest du jetzt schon zum zweiten mal, dann verrate doch mal bitte wo du einen Hack brauchst um in Java Serializable zu implementieren. Was kannst du den nicht machen? Wo schränken dich da die Java Interfaces ein?



  • wathacks schrieb:

    Shade Of Mine schrieb:

    Man braucht sich nur Serializable ansehen. Es ist in Java nicht ohne Hack implementierbar. Das muss einem doch zu denken geben, oder?

    Das behauptest du jetzt schon zum zweiten mal, dann verrate doch mal bitte wo du einen Hack brauchst um in Java Serializable zu implementieren. Was kannst du den nicht machen? Wo schränken dich da die Java Interfaces ein?

    Das ist ein Scherz oder?
    Schau dir mal an wie writeObject und readObject integriert wurden.

    Die JVM hat eine extra spezialbehandlung eingeführt. Etwas dass Clientcode nicht machen kann. Die Benutzung ist zwar super einfach - aber die Technik dahinter - eben _wegen_ den Java Interfaces ein Horror.



  • Shade Of Mine schrieb:

    Das ist ein Scherz oder?

    Du hast es immer noch nicht kapiert, oder?



  • Shade Of Mine schrieb:

    wathacks schrieb:

    Shade Of Mine schrieb:

    Man braucht sich nur Serializable ansehen. Es ist in Java nicht ohne Hack implementierbar. Das muss einem doch zu denken geben, oder?

    Das behauptest du jetzt schon zum zweiten mal, dann verrate doch mal bitte wo du einen Hack brauchst um in Java Serializable zu implementieren. Was kannst du den nicht machen? Wo schränken dich da die Java Interfaces ein?

    Das ist ein Scherz oder?
    Schau dir mal an wie writeObject und readObject integriert wurden.

    Die JVM hat eine extra spezialbehandlung eingeführt. Etwas dass Clientcode nicht machen kann. Die Benutzung ist zwar super einfach - aber die Technik dahinter - eben _wegen_ den Java Interfaces ein Horror.

    Wieso wegen den Java Interfaces? Das man ein paar Hacks brauch, um auf private member beliebiger Klassen zugreifen zu können ist klar. Aber was hat das mit den Interfaces zu tun? Wenn du das ohne diese Tricks machen willst, dann musst du halt ein Interface mit write... und read... machen und in jeder Klasse die das implementiert müssen dann die member per hand in den stream gesteckt werden. Ist dann nicht mehr so komfortabel, aber geht eigentlich ganz einfach mit den Java Interfaces. Oder was stellst du dir für eine Interface definition vor mit der das irgendwie anders geht?


Anmelden zum Antworten