it-swarm.dev

Warum haben speicherverwaltete Sprachen wie Java, Javascript und C # das Schlüsselwort "new" beibehalten?

Das Schlüsselwort new in Sprachen wie Java, Javascript und C # erstellt eine neue Instanz einer Klasse.

Diese Syntax scheint von C++ geerbt worden zu sein, wobei new speziell verwendet wird, um eine neue Instanz einer Klasse auf dem Heap zuzuweisen und einen Zeiger auf die neue Instanz zurückzugeben. In C++ ist dies nicht die einzige Möglichkeit, ein Objekt zu erstellen. Sie können auch ein Objekt auf dem Stapel erstellen, ohne new zu verwenden. Tatsächlich ist diese Art der Objektkonstruktion in C++ weitaus häufiger.

Aus einem C++ - Hintergrund stammend, erschien mir das Schlüsselwort new in Sprachen wie Java, Javascript und C # natürlich und offensichtlich. Dann fing ich an, Python zu lernen, das nicht das Schlüsselwort new hat. In Python wird eine Instanz einfach durch Aufrufen des Konstruktors erstellt, z.

f = Foo()

Zuerst schien mir das ein bisschen abwegig zu sein, bis mir einfiel, dass es keinen Grund für Python, new zu haben, weil alles ein Objekt ist und es daher nicht nötig ist zwischen verschiedenen Konstruktorsyntaxen unterscheiden.

Aber dann dachte ich - was ist wirklich der Sinn von new in Java? Warum sollten wir Object o = new Object(); sagen? Warum nicht einfach Object o = Object();? In C++ besteht definitiv ein Bedarf an new, da wir zwischen der Zuweisung auf dem Heap und der Zuweisung auf dem Stapel unterscheiden müssen, aber in Java werden alle Objekte auf dem Heap erstellt). Warum also überhaupt das Schlüsselwort new? Die gleiche Frage könnte für Javascript gestellt werden. In C #, mit dem ich viel weniger vertraut bin, denke ich, dass new einen Zweck in Bezug auf die Unterscheidung haben kann zwischen Objekttypen und Werttypen, aber ich bin nicht sicher.

Unabhängig davon scheint es mir, dass viele Sprachen, die nach C++ kamen, einfach das Schlüsselwort new "geerbt" haben - ohne es wirklich zu brauchen. Es ist fast wie ein Schlüsselwort . Wir scheinen es aus keinem Grund zu brauchen, und doch ist es da.

Frage : Bin ich richtig? Oder gibt es einen zwingenden Grund, warum new in C++ - inspirierten speicherverwalteten Sprachen wie Java, Javascript und C # sein muss, aber nicht Python?

141
Channel72

Ihre Beobachtungen sind richtig. C++ ist eine komplizierte Bestie, und das Schlüsselwort new wurde verwendet, um zwischen etwas zu unterscheiden, das später delete benötigt, und etwas, das automatisch zurückgefordert wird. In Java und C #) haben sie das Schlüsselwort delete gelöscht, da der Garbage Collector sich für Sie darum kümmern würde.

Das Problem ist dann, warum sie das Schlüsselwort new beibehalten haben. Ohne mit den Leuten zu sprechen, die die Sprache geschrieben haben, ist es schwierig zu antworten. Meine besten Vermutungen sind unten aufgeführt:

  • Es war semantisch richtig. Wenn Sie mit C++ vertraut waren, wussten Sie, dass das Schlüsselwort new ein Objekt auf dem Heap erstellt. Warum also das erwartete Verhalten ändern?
  • Es macht darauf aufmerksam, dass Sie ein Objekt instanziieren, anstatt eine Methode aufzurufen. Bei Empfehlungen im Microsoft-Codestil beginnen Methodennamen mit Großbuchstaben, sodass Verwirrung entstehen kann.

Ruby liegt irgendwo zwischen Python und Java/C # in der Verwendung von new. Grundsätzlich instanziieren Sie ein Objekt wie folgt:

f = Foo.new()

Es ist kein Schlüsselwort, sondern eine statische Methode für die Klasse. Wenn Sie einen Singleton möchten, können Sie die Standardimplementierung von new() überschreiben, um jedes Mal dieselbe Instanz zurückzugeben. Es ist nicht unbedingt zu empfehlen, aber es ist möglich.

97
Berin Loritsch

Kurz gesagt, Sie haben Recht. Das Schlüsselwort new ist in Sprachen wie Java und C #) überflüssig. Hier einige Erkenntnisse von Bruce Eckel, der in den 1990er Jahren Mitglied des C++ Standard Comitee war und später Bücher über Java veröffentlichte: http: //www.artima.com/weblogs/viewpost.jsp?thread=260578

es musste eine Möglichkeit geben, Heap-Objekte von Stack-Objekten zu unterscheiden. Um dieses Problem zu lösen, wurde das neue Schlüsselwort von Smalltalk übernommen. Um ein Stapelobjekt zu erstellen, deklarieren Sie es einfach wie in Cat x; oder mit Argumenten Cat x ("Fäustlinge");. Um ein Heap-Objekt zu erstellen, verwenden Sie new wie in new Cat. oder neue Katze ("Fäustlinge");. Angesichts der Einschränkungen ist dies eine elegante und konsistente Lösung.

Geben Sie Java ein, nachdem Sie entschieden haben, dass alles in C++ schlecht gemacht und zu komplex ist. Die Ironie hier ist, dass Java konnte und hat die Entscheidung getroffen, die Stapelzuweisung wegzuwerfen (wobei das Debakel der Grundelemente, das ich an anderer Stelle angesprochen habe, ausdrücklich ignoriert wurde). Und da alle Objekte auf dem zugeordnet sind Heap, es besteht keine Notwendigkeit, zwischen Stapel- und Heap-Zuordnung zu unterscheiden. Sie hätten leicht Cat x = Cat () oder Cat x = Cat ("Fäustlinge") sagen können. Oder noch besser, integrierte Typinferenz, um die Wiederholung zu eliminieren (aber das - - und andere Funktionen wie Schließungen - hätten "zu lange" gedauert, so dass wir bei der mittelmäßigen Version von Java; Typinferenz wurde diskutiert, aber ich werde die Wahrscheinlichkeit, dass dies nicht der Fall ist, festhalten) festhalten passieren und sollte angesichts der Probleme beim Hinzufügen neuer Funktionen zu Java nicht).

87

Ich kann mir zwei Gründe vorstellen:

  • new unterscheidet zwischen einem Objekt und einem Grundelement
  • Die Syntax new ist etwas besser lesbar (IMO)

Der erste ist trivial. Ich denke nicht, dass es schwieriger ist, die beiden so oder so auseinander zu halten. Das zweite ist ein Beispiel für den Punkt des OP, nämlich dass new irgendwie redundant ist.

Es kann jedoch zu Namespace-Konflikten kommen. Erwägen:

public class Foo {
   Foo() {
      // Constructor
   }
}

public class Bar {
   public static void main(String[] args) {
      Foo f = Foo();
   }

   public Foo Foo() {
      return Foo();
   }
}

Sie könnten, ohne die Vorstellungskraft zu sehr zu dehnen, leicht zu einem unendlich rekursiven Aufruf führen. Der Compiler müsste den Fehler "Funktionsname wie Objekt" erzwingen. Das würde wirklich nicht schaden, aber es ist viel einfacher, new zu verwenden, das besagt, Go to the object of the same name as this method and use the method that matches this signature. Java ist eine sehr einfach zu analysierende Sprache, und ich vermute, dass dies in diesem Bereich hilfreich ist.

15
Michael K

Zum einen hat Java immer noch die Dichotomie von C++: nicht ganz alles ist ein Objekt. Java verfügt über integrierte Typen (z. B. char und int), die nicht dynamisch zugewiesen werden müssen.

Das bedeutet nicht, dass new in Java) wirklich notwendig ist - die Menge der Typen, die nicht dynamisch zugewiesen werden müssen, ist fest und bekannt. Wenn Sie definieren Ein Objekt, das der Compiler wissen könnte (und tatsächlich weiß), ob es sich um einen einfachen Wert handelt, der char ist, oder um ein Objekt, das dynamisch zugewiesen werden muss.

Ich werde (noch einmal) nicht darüber nachdenken, was dies für die Qualität des Java-Designs bedeutet (oder gegebenenfalls das Fehlen davon).

10
Jerry Coffin

In JavaScript benötigen Sie es, da der Konstruktor wie eine normale Funktion aussieht. Woher sollte JS also wissen, dass Sie ein neues Objekt erstellen möchten, wenn das neue Schlüsselwort nicht vorhanden wäre?

Das Aufrufen einer JavaScript-Funktion mit dem Operator new führt zu einem anderen Verhalten als das Aufrufen einer Funktion ohne den Operator new.

Zum Beispiel:

Date()       //Returns a string

new Date()   //Returns a Date object
function foo() {};

foo()        //Returns `undefined`

new foo()    //Returns an empty object
10
user281377

Ich mag viele Antworten und möchte nur Folgendes hinzufügen:
Erleichtert das Lesen von Code
Nicht, dass diese Situation häufig vorkommen würde, aber denken Sie, Sie wollten eine Methode im Namen eines Verbs, das denselben Namen wie ein Substantiv hat. C # und andere Sprachen haben natürlich das Wort object reserviert. Wenn dies nicht der Fall wäre, wäre Foo = object() das Ergebnis des Objektmethodenaufrufs oder würde ein neues Objekt instanziiert. Hoffentlich hat die Sprache ohne das Schlüsselwort new Schutz vor dieser Situation, aber wenn Sie das Schlüsselwort new vor dem Aufruf eines Konstruktors benötigen, können Sie die Existenz einer Methode mit demselben Namen wie zulassen ein Objekt.

4
Kavet Kerek

Interessanterweise braucht VB tut es - die Unterscheidung zwischen

Dim f As Foo

die eine Variable deklariert, ohne sie zuzuweisen, das Äquivalent von Foo f; in C # und

Dim f As New Foo()

das Deklarieren der Variablen und Zuweisen einer neuen Instanz der Klasse, das Äquivalent von var f = new Foo(); in C #, ist eine wesentliche Unterscheidung. In pre-.NET VB können Sie sogar Dim f As Foo Oder Dim f As New Foo Verwenden, da Konstruktoren nicht überladen wurden.

4
Richard Gadsden

In vielen Programmiersprachen gibt es viele Überreste. Manchmal ist es so konzipiert (C++ wurde so konzipiert, dass es mit C so kompatibel wie möglich ist), manchmal ist es einfach da. Betrachten Sie für ein ungeheuerlicheres Beispiel, wie weit sich die gebrochene C switch -Anweisung verbreitet hat.

Ich kenne Javascript und C # nicht gut genug, um es zu sagen, aber ich sehe keinen Grund für new in Java, außer dass C++ es hatte.

3
David Thornley

In C # können Typen in Kontexten angezeigt werden, in denen sie andernfalls von einem Mitglied verdeckt werden könnten. Mit können Sie eine Eigenschaft mit demselben Namen wie ihren Typ festlegen. Folgendes berücksichtigen,

class Address { 
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

class Person {
    public string Name { get; set; }
    public Address Address { get; set; }

    public void ChangeStreet(string newStreet) {
        Address = new Address { 
            Street = newStreet, 
            City = Address.City,
            State = Address.State,
            Zip = Address.Zip
        };
    }
}

Beachten Sie, dass durch die Verwendung von new klar wird, dass Address der Typ Address und nicht das Mitglied Address ist. Dadurch kann ein Mitglied denselben Namen wie sein Typ haben. Ohne dies müssten Sie dem Namen des Typs ein Präfix voranstellen, um die Namenskollision zu vermeiden, z. B. CAddress. Dies war sehr beabsichtigt, da Anders die ungarische Notation oder ähnliches nie mochte und wollte, dass C # ohne sie verwendet werden kann. Die Tatsache, dass es auch bekannt war, war ein doppelter Bonus.

3
chuckj

Ich bin mir nicht sicher, ob Java Designer dies im Sinn hatten, aber wenn Sie Objekte als rekursive Datensätze betrachten, können Sie sich vorstellen, dass Foo(123) gibt kein Objekt zurück, sondern eine Funktion, deren Fixpunkt das zu erstellende Objekt ist (dh eine Funktion, die das Objekt zurückgibt, wenn das zu konstruierende Objekt als Argument angegeben wird). Der Zweck von new besteht darin, "zu binden" den Knoten "und geben Sie diesen Fixpunkt zurück. Mit anderen Worten, new würde das" zukünftige Objekt "auf sein self aufmerksam machen.

Diese Art von Ansatz könnte beispielsweise bei der Formalisierung der Vererbung hilfreich sein: Sie könnten eine Objektfunktion um eine andere Objektfunktion erweitern und sie schließlich mit self mit new versehen:

cp = new (Colored("Blue") & Line(0, 0, 100, 100))

Hier & kombiniert zwei Objektfunktionen.

In diesem Fall könnte new definiert werden als:

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}
0
Aivar

Interessanterweise ist mit dem Aufkommen der perfekten Weiterleitung auch in C++ keine Nichtplatzierung new erforderlich. Es wird also wohl in keiner der genannten Sprachen mehr benötigt.

0
DeadMG