Perché i linguaggi gestiti dalla memoria come Java, Javascript e C # mantengono la parola chiave 'nuova'?

136

La parola chiave new in lingue come Java, Javascript e C # crea una nuova istanza di una classe.

Questa sintassi sembra essere stata ereditata da C ++, dove new è usato specificamente per allocare una nuova istanza di una classe sull'heap e restituire un puntatore alla nuova istanza. In C ++, questo non è l'unico modo per costruire un oggetto. Puoi anche costruire un oggetto sullo stack, senza usare new - e infatti, questo modo di costruire oggetti è molto più comune in C ++.

Quindi, provenendo da uno sfondo C ++, la parola chiave new in lingue come Java, Javascript e C # mi è sembrata naturale e ovvia. Poi ho iniziato a imparare Python, che non ha la parola chiave new . In Python, un'istanza è costruita semplicemente chiamando il costruttore, come:

f = Foo()

All'inizio questo mi è sembrato un po 'strano, fino a quando non mi è venuto in mente che non c'è motivo per Python di avere new , perché tutto è un oggetto quindi non c'è bisogno di disambiguare tra le varie sintassi del costruttore.

Ma poi ho pensato - qual è davvero il punto di new in Java? Perché dovremmo dire Object o = new Object(); ? Perché non solo Object o = Object(); ? In C ++ c'è sicuramente bisogno di new , dal momento che dobbiamo distinguere tra allocare sull'heap e allocare sullo stack, ma in Java tutti gli oggetti sono costruiti sull'heap, quindi perché avere anche la parola chiave new ? La stessa domanda potrebbe essere richiesta per Javascript. In C #, a cui sono molto meno familiare, penso che new possa avere qualche scopo in termini di distinzione tra tipi di oggetti e tipi di valore, ma non ne sono sicuro.

Indipendentemente da ciò, mi sembra che molte lingue che sono venute dopo C ++ semplicemente "ereditassero" la parola chiave new - senza realmente averne bisogno. È quasi come una parola chiave residua . Non sembra che ce ne sia bisogno per nessuna ragione, eppure è lì.

Domanda: ho ragione su questo? O c'è una ragione convincente che new deve essere in C ++ - linguaggi di gestione della memoria ispirati come Java, Javascript e C # ma non Python?

    
posta Channel72 14.04.2014 - 02:20
fonte

15 risposte

92

Le tue osservazioni sono corrette. Il C ++ è una bestia complicata e la parola chiave new è stata usata per distinguere tra qualcosa che aveva bisogno di delete più tardi e qualcosa che sarebbe stato automaticamente recuperato. In Java e C #, hanno abbandonato la parola chiave delete perché il garbage collector si prenderà cura di esso per te.

Il problema allora è perché hanno mantenuto la parola chiave new ? Senza parlare con le persone che hanno scritto la lingua è difficile rispondere. Le mie migliori ipotesi sono elencate di seguito:

  • Era semanticamente corretto. Se avevi familiarità con C ++, sapevi che la parola chiave new crea un oggetto sull'heap. Quindi, perché cambiare il comportamento previsto?
  • Richiama l'attenzione sul fatto che si sta creando un'istanza di un oggetto anziché chiamare un metodo. Con i consigli di stile del codice Microsoft, i nomi dei metodi iniziano con lettere maiuscole, quindi può esserci confusione.

Ruby è una via di mezzo tra Python e Java / C # nel suo utilizzo di new . Fondamentalmente si istanzia un oggetto come questo:

f = Foo.new()

Non è una parola chiave, è un metodo statico per la classe. Ciò significa che se si desidera un singleton, è possibile sovrascrivere l'implementazione predefinita di new() per restituire la stessa istanza ogni volta. Non è necessariamente consigliato, ma è possibile.

    
risposta data 14.02.2011 - 16:58
fonte
86

In breve, hai ragione. Keyword new è superfluo in linguaggi come Java e C #. Ecco alcuni spunti di Bruce Eckel che è stato membro del C ++ Standard Comitee negli anni '90 e in seguito ha pubblicato libri su Java: link

there needed to be some way to distinguish heap objects from stack objects. To solve this problem, the new keyword was appropriated from Smalltalk. To create a stack object, you simply declare it, as in Cat x; or, with arguments, Cat x("mittens");. To create a heap object, you use new, as in new Cat; or new Cat("mittens");. Given the constraints, this is an elegant and consistent solution.

Enter Java, after deciding that everything C++ is badly done and overly complex. The irony here is that Java could and did make the decision to throw away stack allocation (pointedly ignoring the debacle of primitives, which I've addressed elsewhere). And since all objects are allocated on the heap, there's no need to distinguish between stack and heap allocation. They could easily have said Cat x = Cat() or Cat x = Cat("mittens"). Or even better, incorporated type inference to eliminate the repetition (but that -- and other features like closures -- would have taken "too long" so we are stuck with the mediocre version of Java instead; type inference has been discussed but I will lay odds it won't happen. And shouldn't, given the problems in adding new features to Java).

    
risposta data 14.02.2011 - 18:00
fonte
15

Ci sono due ragioni per cui posso pensare:

  • new distingue tra un oggetto e un primitivo
  • La sintassi new è un po 'più leggibile (IMO)

Il primo è banale. Non credo sia più difficile distinguere i due in entrambi i casi. Il secondo è un esempio del punto dell'OP, ovvero che new è ridondante.

Tuttavia, ci potrebbero essere conflitti nello spazio dei nomi; prendere in considerazione:

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

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

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

Potresti, senza troppa fatica, finire facilmente con una chiamata infinitamente ricorsiva. Il compilatore dovrebbe imporre un errore "Nome funzione uguale all'oggetto". In realtà non sarebbe male, ma è molto più semplice usare new , che afferma, Go to the object of the same name as this method and use the method that matches this signature. Java è un linguaggio molto semplice da analizzare, e sospetto che questo aiuti in quell'area.

    
risposta data 30.10.2012 - 13:15
fonte
10

Java, per esempio, ha ancora la dicotomia di C ++: non abbastanza tutto è un oggetto. Java ha tipi predefiniti (ad esempio char e int ) che non devono essere allocati dinamicamente.

Ciò non significa che new sia realmente necessario in Java, tuttavia l'insieme di tipi che non devono essere allocati dinamicamente è fisso e noto. Quando definisci un oggetto, il compilatore può sapere (e, di fatto, lo sa) se si tratta di un valore semplice che char o un oggetto che deve essere assegnato dinamicamente.

Mi asterrò (ancora una volta) dall'affermare cosa significhi sulla qualità del design di Java (o sulla sua mancanza, a seconda dei casi).

    
risposta data 14.02.2011 - 17:17
fonte
8

In JavaScript ti serve perché il costruttore sembra una funzione normale, quindi come dovrebbe JS sapere che vuoi creare un nuovo oggetto se non fosse per la nuova parola chiave?

    
risposta data 14.02.2011 - 21:57
fonte
4

È interessante notare che VB ha bisogno di esso - la distinzione tra

Dim f As Foo

che dichiara una variabile senza assegnarla, l'equivalente di Foo f; in C # e

Dim f As New Foo()

che dichiara la variabile e assegna una nuova istanza della classe, l'equivalente di var f = new Foo(); in C #, è una distinzione sostanziale. In Pre.NET VB, potresti anche usare Dim f As Foo o Dim f As New Foo - perché non c'era sovraccarico sui costruttori.

    
risposta data 14.02.2011 - 21:38
fonte
4

Mi piacciono molte risposte e vorrei solo aggiungere questo:
Semplifica la lettura del codice
Non che questa situazione si verifichi spesso, ma considera di volere un metodo nel nome di un verbo che condivide lo stesso nome di un nome. C # e altre lingue hanno naturalmente la parola object riservata. Ma se così non fosse, Foo = object() sarebbe il risultato della chiamata al metodo oggetto o sarebbe istanziazione di un nuovo oggetto. Si spera che la lingua senza la parola chiave new abbia protezioni contro questa situazione, ma avendo il requisito della parola chiave new prima della chiamata di un costruttore, si consente l'esistenza di un metodo con lo stesso nome di un oggetto.

    
risposta data 15.02.2011 - 14:32
fonte
3

Ci sono molte cose rudimentali in molti linguaggi di programmazione. A volte è presente in base alla progettazione (il C ++ è stato progettato per essere il più compatibile possibile con C), a volte è solo lì. Per un esempio più eclatante, considera la propagazione dell'istruzione C switch rotta.

Non conosco Javascript e C # abbastanza bene da dire, ma non vedo alcun motivo per new in Java tranne che nel C ++.

    
risposta data 14.02.2011 - 16:45
fonte
3

In C # consente tipi visibili in contesti in cui potrebbero altrimenti essere oscurati da un membro. Permette di avere una proprietà con lo stesso nome di un suo tipo. Considera quanto segue,

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
        };
    }
}

Nota che l'uso di new consente di essere chiaro che Address è il tipo Address non il membro Address . Ciò consente a un membro di avere lo stesso nome del suo tipo. Senza questo dovresti anteporre il nome del tipo per evitare la collisione del nome, come CAddress . Questo è stato molto intenzionale in quanto ad Anders non è mai piaciuta la notazione ungherese o qualcosa di simile e voleva che C # fosse utilizzabile senza di essa. Il fatto che fosse anche familiare era un doppio vantaggio.

    
risposta data 12.05.2012 - 05:37
fonte
0

È interessante notare che, con l'avvento dell'inoltro perfetto, non è richiesto nemmeno il non posizionamento new in C ++. Quindi, probabilmente, non è più necessario in nessuna delle lingue menzionate.

    
risposta data 27.10.2011 - 21:35
fonte
0

Non sono sicuro che i progettisti Java lo avessero in mente, ma quando pensi agli oggetti come record ricorsivi , puoi immaginare che Foo(123) non restituisca un oggetto ma una funzione di cui fixpoint è l'oggetto che si sta creando (cioè la funzione che restituirebbe l'oggetto se è dato come argomento l'oggetto che si sta costruendo). E lo scopo di new è di "annodare il nodo" e restituire quel punto di fissazione. In altre parole new renderebbe il "oggetto-a-essere" consapevole del suo self .

Questo tipo di approccio potrebbe aiutare a formalizzare l'ereditarietà ad esempio: potresti estendere una funzione oggetto con un'altra funzione oggetto e infine fornirgli il comune self usando new :

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

Qui & combina due funzioni oggetto.

In questo caso new potrebbe essere definito come:

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}
    
risposta data 31.10.2011 - 16:15
fonte
-2

Per consentire 'nil'.

Insieme all'assegnazione basata su heap, Java ha abbracciato l'uso di oggetti nulli. Non solo come artefatto, ma come caratteristica. Quando gli oggetti possono avere uno stato nullo, è necessario separare la dichiarazione dall'inizializzazione. Da qui la nuova parola chiave.

In C ++ una riga come

Person me;

Chiamerebbe immediatamente il costruttore predefinito.

    
risposta data 29.10.2012 - 07:41
fonte
-2

Il motivo per cui Python non ne ha bisogno è a causa del suo sistema di tipi. Fondamentalmente, quando vedi foo = bar() in Python, non importa se bar() è un metodo che viene invocato, una classe che viene istanziata, o un oggetto functor; in Python, non vi è alcuna differenza fondamentale tra un funtore e una funzione (perché i metodi sono oggetti); e si potrebbe anche obiettare che una classe istanziata è un caso speciale di un funtore. Pertanto, non vi è alcun motivo per creare una differenza artificiale in ciò che sta accadendo (soprattutto perché la gestione della memoria è estremamente nascosta in Python).

In una lingua con un diverso sistema di battitura, o in cui metodi e classi non sono oggetti, c'è la possibilità di una differenza concettuale più strong tra la creazione di un oggetto e la chiamata di una funzione o di un functor. Pertanto, anche se il compilatore / interprete non ha bisogno della parola chiave new , è comprensibile che possa essere mantenuto in linguaggi che non hanno superato tutti i limiti di Python.

    
risposta data 29.01.2013 - 22:32
fonte
-4

In realtà in Java c'è una differenza quando si usano le stringhe:

String myString = "myString";

è diverso da

String myString = new String("myString");

Supponendo che la stringa "myString" non sia mai stata usata prima, la prima istruzione crea un singolo oggetto String nel pool String (un'area speciale dell'heap) mentre la seconda istruzione crea due oggetti, uno nella normale area heap per oggetti e altro nel pool di stringhe. È abbastanza ovvio che la seconda affermazione è inutile in questo particolare scenario, ma è consentita dal linguaggio.

Ciò significa che il nuovo assicura che l'oggetto sia creato in quell'area speciale dell'heap, allocata per la normale istanziazione dell'oggetto, ma potrebbero esserci altri modi per creare un'istanza di oggetti senza di essa; quello sopra è un esempio e c'è spazio per l'estensibilità.

    
risposta data 31.10.2012 - 10:06
fonte
-8

In C # new è importante distinguere tra oggetti creati nello stack rispetto all'heap. Spesso creiamo oggetti sullo stack per prestazioni migliori. Vedete, quando un oggetto sulla pila esce dal campo di applicazione, scompare immediatamente quando la pila si disfa, proprio come una primitiva. Non è necessario che il garbage collector tenga traccia di esso e non vi è alcun overhead aggiuntivo del gestore della memoria per allocare lo spazio necessario sull'heap.

    
risposta data 29.10.2012 - 03:50
fonte

Leggi altre domande sui tag