Warum sind Zeichenfolgen in einigen Sprachen unveränderlich?

9

String ist eine unveränderliche Klasse in Java. Eine unveränderliche Klasse ist einfach eine Klasse, deren Instanzen nicht geändert werden können. Warum wählt die Java-Programmiersprache Objekte der Klasse String unveränderlich?

Tupledev
quelle
2
@PJTraill Es scheint überhaupt nicht unvermeidlich. String-Literale in anderen Sprachen sind beispielsweise in C nicht unveränderlich, und Objekte anderer Klassen in Java sind nicht unveränderlich.
David Richerby
2
Dies ist eine Frage zum Design von Programmiersprachen. Es scheint mir thematisch zu sein.
David Richerby
2
@DavidRicherby, String-Literale sind in C unveränderlich (In C90-Begriffen: Wenn das Programm versucht, ein String-Literal einer der beiden Formen zu ändern, ist das Verhalten undefiniert. Einige frühere Versionen haben es akzeptiert, da in der Sprache keine Konstante vorhanden ist manchmal, was der Programmierer erwartet hatte, aber ich glaube nicht, dass es jemals unterstützt wurde) Ich denke, jeder hat aus dem Fehler im frühen FORTRAN gelernt, der es erlaubte, Literale zu ändern. Wenn Literale veränderbare neue Objekte erstellen, deren Anfangswert dem Literal entspricht, wenn nicht etwas Unklares.
AProgrammer
1
@AProgrammer Es wurde viel darüber geschrieben, warum Java so entworfen wurde, wie es war: Ich wäre überrascht, wenn die Entwurfsentscheidungen für die String-Klasse nicht maßgeblich sind. Aber selbst wenn die Sprachdesigner nie gesagt haben, warum String unveränderlich ist, macht dies die Frage nicht unangebracht oder sogar schlecht: Es bedeutet nur, dass die einzig richtige Antwort leider "Wir wissen es nicht" lautet.
David Richerby
1
@ DavidRicherby Es wäre jedoch besser, wenn die Frage sprachunabhängig wäre. Diese Frage kann durch Zitieren einer Aussage eines Java-Entwicklers beantwortet werden. Wir wollen Antworten, die Konzepte erklären.
Raphael

Antworten:

9

Dieses Problem hängt stark mit der Vorstellung zusammen, was es bedeutet, eine Instanz einer Klasse zu sein. Streng objektorientiert hat eine Klasse eine zugehörige Invariante: ein Prädikat, das beim Beenden einer (öffentlichen) Methode der Klasse immer gilt. Ein solcher Begriff ist von zentraler Bedeutung, um beispielsweise sicherzustellen, dass die Vererbung genau definiert ist (er ist Teil des Liskov-Substitutionsprinzips ).

Eines der schädlichsten Probleme mit Java ist, dass es schwierig ist zu verhindern, dass Clientcode Klasseninvarianten bricht.

Betrachten Sie beispielsweise die folgende 'ZipCode'-Klasse:

class ZipCode {
    private String zipCode;

    public ZipCode(String value){
        if(!isValidZipCode(value))
            throw new IllegalArgumentException();
        zipCode = value;
        assert(invariant());
    }

    public String get() { return zipCode; }

    public boolean invariant() {
        return isValidZipCode( zipCode );
    }
}

Wenn String nicht unveränderlich wäre, könnte ein Benutzer von ZipCode jederzeit 'get' aufrufen und die Zeichen ändern, wodurch die Invariante gebrochen und die konzeptionelle Integrität zerstört wird, die die Kapselung des ZipCode-Konzepts bietet.

Da diese Art von Integrität für die Gewährleistung der Gültigkeit großer Systeme von entscheidender Bedeutung ist, wirft diese Antwort auf Ihre Frage die umfassendere Frage auf:

"Warum unterstützt Java kein Analogon von C ++ const oder bietet zumindest unveränderliche Versionen von mehr Bibliotheksklassen an?"

NietzscheanAI
quelle
7

Dinge wie Zeichenfolgen und Datumsangaben sind natürlich Werte. In C ++ erwarten wir, dass sie einen Kopierkonstruktor, einen Zuweisungsoperator und einen Gleichheitsoperator haben, aber wir erwarten nie, dass sie ihre Adresse annehmen. Daher erwarten wir nicht, dass sie einzeln auf dem Heap zugewiesen werden. Virtuelle Methoden machen keinen Sinn.

Domänenobjekte sind natürlich Referenzen. C ++ - Operatoren haben keinen Kopierkonstruktor, Zuweisungsoperator oder Gleichheitsoperator (sie sind nur dann gleich, wenn sie identisch sind). Wir können ihre Adresse übernehmen und erwarten, dass ihnen ein Heap zugewiesen wird. Methoden sind in der Regel virtuell.

Java hat keine Wertklassen, nur Referenzklassen. Werte werden mit unveränderlichen Objekten gefälscht. Dies gilt für Zeichenfolgen, leider nicht für Daten. Die Veränderbarkeit von Java-Daten hat häufig zu Problemen geführt und ist jetzt veraltet. Veränderbare Werte können beispielsweise nicht als Grundlage für einen Hash verwendet werden.

Miniaturansicht
quelle
Nun, veränderbare Werte können für das Hashing verwendet werden, aber Sie sollten sie später nicht mehr mutieren, wenn Sie sich auf den Hash-Code verlassen haben!
Gnasher729
6

Java wurde entwickelt, um die Ausführung von Unterabschnitten des Programmcodes in Umgebungen mit eingeschränkten Sicherheitsanforderungen zu ermöglichen. Diese Anforderung wurde implementiert, indem ein "SecurityManager" für einen Thread festgelegt wurde, der Zugriff auf die Parameter bestimmter kritischer Vorgänge erhält (z. B. Öffnen einer Datei), und gefragt wurde, ob der Vorgang ausgeführt werden darf oder nicht. Wenn Java-Zeichenfolgen veränderbar wären, könnte ein Programm solche Einschränkungen umgehen, indem es zwei Threads erstellt, von denen einer eine zulässige Operation zum Öffnen einer Datei ausführt, während die andere die Zeichenfolge, in der der Dateiname gespeichert ist, in einen nicht zulässigen ändert. Es besteht dann die Möglichkeit, dass der Sicherheitsmanager die ursprüngliche Zeichenfolge liest und die Operation akzeptiert, die an den Dateiöffnungscode weitergeleitet wird, der dann vor dem Öffnen der zweiten (nicht zulässigen) Datei angezeigt wird.

  • unveränderliche Saiten
  • Durchführen eines defensiven Kopierens einer sicherheitskritischen Zeichenfolge, bevor deren Akzeptanz überprüft wird.

Die letztere Möglichkeit würde dazu führen, dass alle derartigen Vorgänge langsamer ablaufen und die Implementierung mit größerer Wahrscheinlichkeit Fehler enthält. Daher war die Verwendung unveränderlicher Zeichenfolgen die sinnvollste Entscheidung.

Im Allgemeinen sind unveränderliche Objekte nützlich, da sie die Freigabe ermöglichen, ohne dass defensive Kopien erstellt werden müssen (was auch in nicht sicherheitskritischem Code erforderlich sein kann, um Fehler zu vermeiden, wenn sich die Quelldaten ändern). Daher wäre die Entscheidung auch ohne diese Anforderung weiterhin getroffen eine vernünftige.

Jules
quelle
1
Ich bin froh, dass jemand darauf hingewiesen hat, denn James Gosling war sich dieser Designentscheidung völlig klar. Java wurde so konzipiert, dass Sie nicht vertrauenswürdigen Code ausführen können, der Ihnen über ein Netzwerk gesendet wird (z. B. in einem Webbrowser oder einer digitalen Set-Top-Box). Der Hauptgrund für die Unveränderlichkeit von Zeichenfolgen bestand darin, Anbietern oder Site-Managern (und den Implementierern der Java-Standardbibliothek!) Die Implementierung eigener benutzerdefinierter Sicherheitsrichtlinien zu erleichtern. Unveränderliche Strings schließen einen potenziellen Angriffsvektor effektiv ab.
Pseudonym