web-dev-qa-db-ja.com

Java、Javascript、C#などのメモリ管理言語が `new`キーワードを保持したのはなぜですか?

Java、Javascript、C#などの言語のnewキーワードは、クラスの新しいインスタンスを作成します。

この構文はC++から継承されたようで、newは、ヒープ上のクラスの新しいインスタンスを割り当て、新しいインスタンスへのポインターを返すために使用されます。 C++では、これがオブジェクトを構築する唯一の方法ではありません。 newを使用せずに、スタック上にオブジェクトを構築することもできます。実際、C++では、この方法でオブジェクトを構築する方がはるかに一般的です。

したがって、C++のバックグラウンドから来て、Java、Javascript、C#などの言語のnewキーワードは自然で明白に見えました。それから、newキーワードのないPythonを学び始めました。 Pythonでは、次のようにコンストラクタを呼び出すだけでインスタンスが構築されます。

_f = Foo()
_

すべてがオブジェクトであるため、さまざまなコンストラクター構文を明確にする必要がないため、Pythonにnewを使用する理由がないことに気付くまで、最初はこれは少し気がかりでした。

しかし、私は思った-Javaのnewの本当の意味は何ですか?なぜObject o = new Object();と言うべきですか?なぜObject o = Object();だけではないのですか? C++では、ヒープへの割り当てとスタックへの割り当てを区別する必要があるため、newの必要性は確実にありますが、Javaではすべてのオブジェクトがヒープ上に構築されるので、newキーワードを使用するのはなぜですか?同じ質問はJavascriptのために尋ねられる可能性があります。私があまり馴染みのないC#では、newにはオブジェクトタイプと値タイプを区別する目的があると思いますが、よくわかりません。

いずれにせよ、C++の後に登場した多くの言語は、newキーワードを単に "継承"しているようです-実際には必要ありません。これは、痕跡のあるキーワードのようなものです。何らかの理由でそれを必要としているようには見えませんが、それでもあります。

質問:私はこれについて正しいですか?または、newがPythonではなく、Java、Javascript、C#などのC++にインスパイアされたメモリ管理言語である必要があるという説得力のある理由はありますか?

141
Channel72

あなたの観察は正しいです。 C++は複雑な獣であり、newキーワードは、後でdeleteが必要なものと自動的に再利用されるものを区別するために使用されました。 JavaおよびC#では、ガベージコレクターが自動的に処理するため、deleteキーワードを削除しました。

問題は、なぜnewキーワードを維持したのかということです。言語を書いた人たちと話をしなければ、答えるのはちょっと難しいです。私の最良の推測は以下のとおりです:

  • 意味的に正しかった。 C++に精通している場合は、newキーワードがヒープ上にオブジェクトを作成することを知っています。では、なぜ予想される動作を変更するのでしょうか?
  • これは、メソッドを呼び出すのではなく、オブジェクトをインスタンス化しているという事実に注意を喚起します。 Microsoftコードスタイルの推奨事項では、メソッド名は大文字で始まるため、混乱が生じる可能性があります。

Rubyはnewを使用するPythonとJava/C#の間のどこかにあります。基本的には、次のようにオブジェクトをインスタンス化します。

_f = Foo.new()
_

これはキーワードではなく、クラスの静的メソッドです。つまり、シングルトンが必要な場合は、new()のデフォルト実装をオーバーライドして、毎回同じインスタンスを返すことができます。必ずしも推奨されるわけではありませんが、可能です。

97
Berin Loritsch

要するに、あなたは正しいです。新しいキーワードはJavaやC#などの言語では不要です。1990年代にC++ Standard ComiteeのメンバーであったBruce Eckelからの洞察と、Javaに関するその後の出版物: http: //www.artima.com/weblogs/viewpost.jsp?thread=260578

ヒープオブジェクトとスタックオブジェクトを区別する方法が必要でした。この問題を解決するために、Smalltalkから新しいキーワードが割り当てられました。スタックオブジェクトを作成するには、Cat xのように宣言するだけです。または、引数付きで、Cat x( "mittens");。ヒープオブジェクトを作成するには、newのようにnewを使用します。または新しいCat( "ミトン");。制約があれば、これはエレガントで一貫したソリューションです。

すべてのC++がうまく機能せず、過度に複雑であると判断した後、Javaに入ります。ここでの皮肉なのは、Javaはスタック割り当てを破棄する決定を下すことができたし、実際にそうしたことです。私は他の場所で取り上げたプリミティブの失敗を無視しています)。そして、すべてのオブジェクトがヒープ、スタックとヒープの割り当てを区別する必要はありません。Catx = Cat()またはCat x = Cat( "mittens")と簡単に言うことができます。または、繰り返しを排除するために、型推論を組み込んでいます(ただし- -およびクロージャなどの他の機能-「時間がかかりすぎる」ので、平凡なバージョンのJavaの代わりに立ち往生しています。型の推論については説明しましたが、私はそれを実行しません。 Javaに新機能を追加する際の問題を考えると、そうなるはずはありません。

87

Javaには、C++の二分法がまだあります。notquiteすべてがオブジェクトです。 Javaには、動的に割り当てる必要のない組み込み型(例:charおよびint)があります。

Javaでもnewが本当に必要であるという意味ではありません-動的に割り当てる必要のない型のセットは固定されており、既知です。オブジェクトの場合、コンパイラは、それがcharの単純な値であるか、動的に割り当てる必要があるオブジェクトであるかを知ることができます(実際には知っています)。

Javaの設計の品質(または場合によってはその欠如)について、これが何を意味するかについては(まだ)意見を述べないようにします。

10
Jerry Coffin

JavaScriptでは、コンストラクターは通常の関数のように見えるので必要ですが、JSが新しいオブジェクトではない場合に新しいオブジェクトを作成することをJSがどのように知る必要がありますか?

new演算子を使用してJavaScript関数を呼び出すと、new演算子を使用せずに関数を呼び出す場合とは異なる動作になります。

例えば:

Date()       //Returns a string

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

foo()        //Returns `undefined`

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

私は多くの答えが好きで、これを追加したいだけです:
コードを読みやすくします
このような状況が頻繁に発生するわけではありませんが、名詞と同じ名前を共有する動詞の名前のメソッドが必要だと考えてください。 C#およびその他の言語では、当然、Word objectが予約されています。しかし、そうでなければ、Foo = object()はオブジェクトメソッド呼び出しの結果になるか、新しいオブジェクトをインスタンス化することになります。うまくいけば、newキーワードのない言語はこの状況から保護されますが、コンストラクターを呼び出す前にnewキーワードを要求することで、同じ名前のメソッドの存在を許可しますオブジェクト。

4
Kavet Kerek

興味深いことに、VB doesそれが必要-間の区別

_Dim f As Foo
_

変数を割り当てずに宣言します。C#の_Foo f;_と同等です。

_Dim f As New Foo()
_

変数を宣言し、クラスの新しいインスタンスを割り当てる、C#のvar f = new Foo();に相当するものは、実質的な違いです。 .NET以前のVBでは、コンストラクタにオーバーロードがなかったため、_Dim f As Foo_または_Dim f As New Foo_を使用することもできました。

4
Richard Gadsden

多くのプログラミング言語には多くの痕跡があります。仕様によって存在する場合もあります(C++はCと可能な限り互換性があるように設計されています)。より悪質な例として、壊れたC switchステートメントがどこまで伝播したかを考えてみましょう。

JavascriptとC#について十分な知識がないので、Javaにnewを使用する理由はC++にあること以外はわかりません。

3
David Thornley

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

newを使用すると、AddressAddressメンバーではなくAddressタイプであることが明確になります。これにより、メンバーはそのタイプと同じ名前を持つことができます。これがなければ、CAddressのように、名前の衝突を回避するためにタイプの名前にプレフィックスを付ける必要があります。 Andersはハンガリーの表記法やそれに類似した表記法が好きではなく、C#を使わなくても使えるようにしたかったため、これは非常に意図的なものでした。それもおなじみの事実は二重のボーナスでした。

3
chuckj

Java設計者がこれを念頭に置いていたかどうかはわかりませんが、オブジェクトを再帰的レコードと考えると、そのFoo(123)を想像できますはオブジェクトを返しませんが、作成されるオブジェクトが固定点である関数(つまり、構築されるオブジェクトを引数として指定した場合にオブジェクトを返す関数)です。newの目的は、つまり、newは「存在するオブジェクト」にそのselfを認識させます。

この種のアプローチは、継承を形式化するのに役立ちます。たとえば、オブジェクト関数を別のオブジェクト関数で拡張し、最後にselfを使用して共通のnewを提供できます。

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

ここに &は、2つのオブジェクト関数を組み合わせます。

この場合、newは次のように定義できます。

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

興味深いことに、完全な転送の出現により、C++では非配置newもまったく必要ありません。したがって、おそらく、言及された言語のいずれかではもはや必要ありません。

0
DeadMG