it-swarm-eu.dev

Proč si jazyky spravované pamětí, jako Java, Javascript a C #, uchovaly klíčové slovo `new`?

Klíčové slovo new v jazycích jako Java, Javascript a C # vytváří novou instanci třídy.

Zdá se, že tato syntaxe byla zděděna z C++, kde new se používá konkrétně k přidělení nové instance třídy na haldě a vrací ukazatel na novou instanci. V C++ to není jediný způsob, jak vytvořit objekt. Můžete také vytvořit objekt v zásobníku bez použití new - a ve skutečnosti je tento způsob vytváření objektů v C++ mnohem běžnější.

Takže, pocházím z pozadí C++, mi klíčové slovo new v jazycích jako Java, Javascript a C # připadalo přirozené a zřejmé. Pak jsem se začal učit Python, který nemá klíčové slovo new. V Pythonu je instance konstruována jednoduše voláním konstruktoru, například:

f = Foo()

Nejprve mi to připadalo trochu pryč, dokud mi nenapadlo, že neexistuje žádný důvod pro to, aby Python mít new, protože všechno je objekt, takže není třeba disambiguate mezi různými syntaxemi konstruktoru.

Ale pak jsem si myslel - co je vlastně smyslem new v Javě? Proč bychom měli říkat Object o = new Object();? Proč ne jen Object o = Object();? V C++ je určitě potřeba new, protože musíme rozlišovat mezi alokací na haldě a alokací na zásobníku, ale v Java všechny objekty jsou konstruovány na haldě, tak proč dokonce mít klíčové slovo new? Stejná otázka by mohla být položena pro Javascript. V C #, se kterým jsem mnohem méně obeznámen, si myslím, že new může mít nějaký účel, pokud jde o rozlišení mezi typy objektů a typy hodnot, ale nejsem si jistý.

Bez ohledu na to se mi zdá, že mnoho jazyků, které přišly po C++, jednoduše „zdědilo“ klíčové slovo new - aniž by to opravdu potřebovalo. Je to skoro jako původní klíčové slovo . Zdá se, že to z nějakého důvodu nepotřebujeme, a přesto je to tam.

Otázka: Mám v tom pravdu? Nebo existuje nějaký přesvědčivý důvod, že new musí být v jazycích C++ - inspirované paměti spravované jazyky jako Java, Javascript a C #, ale ne Python?

141
Channel72

Vaše postřehy jsou správné. C++ je komplikované zvíře a klíčové slovo new bylo použito k rozlišení mezi něčím, co později delete bylo potřeba, a něčím, co by bylo automaticky získáno zpět. V Java a C #, oni upustili klíčové slovo delete, protože sběratel odpadu by se o to postaral za vás.

Problém je v tom, proč si ponechali klíčové slovo new? Aniž bychom mluvili s lidmi, kteří psali jazyk, je těžké odpovědět. Moje nejlepší odhady jsou uvedeny níže:

  • Bylo to sémanticky správné. Pokud jste byli obeznámeni s C++, věděli jste, že klíčové slovo new vytváří objekt na haldě. Proč tedy změnit očekávané chování?
  • Upozorňuje na skutečnost, že instanciujete objekt spíše než voláním metody. S doporučeními stylu Microsoft kódu začínají názvy metod velkými písmeny, takže může dojít k záměně.

Ruby je někde mezi Python a Java/C # v jeho použití new. V podstatě instanciujete takový objekt:

f = Foo.new()

Není to klíčové slovo, je to statická metoda pro třídu. To znamená, že pokud chcete singleton, můžete přepsat výchozí implementaci new() a vrátit pokaždé stejnou instanci. Není to nutně doporučeno, ale je to možné.

97
Berin Loritsch

Stručně řečeno, máte pravdu. Nové klíčové slovo je nadbytečné v jazycích jako Java a C #. Zde jsou některé postřehy od Bruce Eckel, který byl členem C++ Standard Comitee v 90. letech a později publikoval knihy o Javě: http: //www.artima.com/weblogs/viewpost.jsp?thread=260578

bylo potřeba nějakým způsobem odlišit haldy od objektů zásobníku. K vyřešení tohoto problému bylo nové klíčové slovo přiděleno z Smalltalk. Chcete-li vytvořit objekt zásobníku, jednoduše jej deklarujte, jako v Cat x; nebo, s argumenty, Cat x ("rukavice") ;. Chcete-li vytvořit objekt haldy, použijte nový, jako u nové kočky; nebo nová kočka („rukavice“) ;. Vzhledem k omezením jde o elegantní a konzistentní řešení.

Vstupte do Java po rozhodnutí, že je vše C++ špatně provedeno a příliš složité. Ironií je, že Java mohl a udělal rozhodnutí vyhodit přidělení zásobníku (ostře ignorovat debakl primitivů, které jsem oslovil jinde). A protože všechny objekty jsou přidělovány na haldy, není třeba rozlišovat mezi alokací zásobníku a haldy. Mohli by snadno říci Cat x = Cat () nebo Cat x = Cat ("palčáky"). Nebo ještě lépe, inferenční typ odvození eliminující opakování (ale to - - a další funkce, jako jsou uzávěry - by to trvalo „příliš dlouho“, takže jsme uvízli s průměrnou verzí Java; byl odvozen typový odvod, ale budu kladen šance, že nebude) a nemělo by se to vzhledem k problémům s přidáváním nových funkcí do Java).

87

Mohu myslet na dva důvody:

  • new rozlišuje mezi objektem a primitivem
  • Syntaxe new je o něco čitelnější (IMO)

První je triviální. Nemyslím si, že je těžší to rozeznat oběma směry. Druhým je příklad bodu OP, což znamená, že new je druh redundantní.

Mohly by však existovat konflikty v oboru názvů; zvážit:

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

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

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

Bez přílišného natažení představivosti byste mohli snadno skončit nekonečně rekurzivním hovorem. Kompilátor by musel vynutit chybu „Název funkce stejný jako objekt“. To by opravdu neublížilo, ale je mnohem jednodušší použít new, které uvádí, Go to the object of the same name as this method and use the method that matches this signature. Java je velmi jednoduchý jazyk, který lze analyzovat, a domnívám se, že to v této oblasti pomáhá).

15
Michael K

Java má například stále dichotomii C++: ne docela všechno je objekt. Java má vestavěné typy (např. char a int), které nemusí být přidělovány dynamicky.

To neznamená, že new je skutečně nutné v Java i když - sada typů, které není třeba dynamicky alokovat) je pevná a známá. objekt, který by kompilátor mohl vědět (a ve skutečnosti to ví), zda jde o jednoduchou hodnotu, kterou char, nebo o objekt, který musí být přidělen dynamicky.

Budu se zdržet (ještě jednou) snahy o to, co to znamená o kvalitě Java designu (případně jeho nedostatku).

10
Jerry Coffin

V JavaScriptu to potřebujete, protože konstruktor vypadá jako normální funkce, tak jak by měla JS vědět, že chcete vytvořit nový objekt, pokud to nebylo pro nové klíčové slovo?

Vyvolání funkce JavaScriptu s operátorem new má za následek jiné chování než vyvolání funkce bez operátoru new.

Například:

Date()       //Returns a string

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

foo()        //Returns `undefined`

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

Mám rád spoustu odpovědí a chtěl bych jen přidat toto:
snadňuje čtení kód
Ne že by k této situaci došlo často, ale zvažte, že jste chtěli metodu ve jménu slovesa, které sdílí stejné jméno jako podstatné jméno. C # a další jazyk mají samozřejmě slovo object rezervované. Pokud by tomu tak ale nebylo, znamenalo by to, že Foo = object() by byl výsledkem volání metody objektu nebo by to bylo instancí nového objektu. Doufejme, že řečený jazyk bez klíčového slova new má ochranu před touto situací, ale požadavkem klíčového slova new před voláním konstruktoru povolíte existenci metody se stejným názvem jako objekt.

4
Kavet Kerek

Je zajímavé, že VB nemá to potřebuje - rozdíl mezi

Dim f As Foo

který deklaruje proměnnou bez přiřazení, ekvivalent Foo f; v C # a

Dim f As New Foo()

který deklaruje proměnnou a přiřadí novou instanci třídy, ekvivalent var f = new Foo(); v C #, je podstatným rozlišením. V pre-.NET VB můžete dokonce použít Dim f As Foo Nebo Dim f As New Foo - protože na konstruktorech nedošlo k přetížení.

4
Richard Gadsden

V mnoha programovacích jazycích je spousta pozůstatků. Někdy je to záměrné (C++ byl navržen tak, aby byl co nejvíce kompatibilní s C), někdy je to jen tam. Pro více nesnadný příklad, zvažte, jak daleko se zlomil C switch propagoval.

Nevím dostatečně Javascript a C #, abych řekl, ale nevidím důvod pro new v Java s výjimkou toho, že to C++ měla.

3
David Thornley

V C # umožňuje typy viditelné v kontextech, kde by je jinak mohl člen zakrýt. Umožní vám mít vlastnost se stejným názvem jako jeho typ. Zvažte následující,

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

Všimněte si, že použití new umožňuje, aby bylo jasné, že Address je typ Address, nikoli člen Address. To umožňuje členovi mít stejný název jako jeho typ. Bez tohoto byste museli předponu názvu typu předcházet kolizi názvu, například CAddress. To bylo velmi úmyslné, protože Anders nikdy neměl rád maďarský zápis nebo něco podobného a chtěl, aby C # byl použitelný bez něj. Skutečnost, že to bylo také známé, byl dvojitý bonus.

3
chuckj

Nejsem si jistý, zda to měli na mysli designéři Java, ale když si myslíte, že jsou objekty rekurzivní záznamy, můžete si představit, že Foo(123) nevrací objekt, ale funkci, jejíž fixpoint je objekt, který je vytvářen (tj. funkce, která by vrátila objekt, pokud je zadán jako argument objekt, který je konstruován). Účelem new je „uvázat“ uzel "a vrátí tento fixpoint. Jinými slovy, new by" object-to-be "uvědomil jeho self.

Tento druh přístupu by mohl pomoci například při formalizaci dědičnosti: můžete rozšířit objektovou funkci o jinou objektovou funkci a nakonec jim poskytnout běžnou self pomocí new:

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

Tady & kombinuje dvě funkce objektu.

V tomto případě lze new definovat jako:

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

Zajímavé je, že s příchodem dokonalého předávání není v C++ vůbec nutné umístit new. V žádném ze zmíněných jazyků tedy již není potřeba.

0
DeadMG