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++にインスパイアされたメモリ管理言語である必要があるという説得力のある理由はありますか?
あなたの観察は正しいです。 C++は複雑な獣であり、new
キーワードは、後でdelete
が必要なものと自動的に再利用されるものを区別するために使用されました。 JavaおよびC#では、ガベージコレクターが自動的に処理するため、delete
キーワードを削除しました。
問題は、なぜnew
キーワードを維持したのかということです。言語を書いた人たちと話をしなければ、答えるのはちょっと難しいです。私の最良の推測は以下のとおりです:
new
キーワードがヒープ上にオブジェクトを作成することを知っています。では、なぜ予想される動作を変更するのでしょうか?Rubyはnew
を使用するPythonとJava/C#の間のどこかにあります。基本的には、次のようにオブジェクトをインスタンス化します。
_f = Foo.new()
_
これはキーワードではなく、クラスの静的メソッドです。つまり、シングルトンが必要な場合は、new()
のデフォルト実装をオーバーライドして、毎回同じインスタンスを返すことができます。必ずしも推奨されるわけではありませんが、可能です。
要するに、あなたは正しいです。新しいキーワードは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に新機能を追加する際の問題を考えると、そうなるはずはありません。
Javaには、C++の二分法がまだあります。notquiteすべてがオブジェクトです。 Javaには、動的に割り当てる必要のない組み込み型(例:char
およびint
)があります。
Javaでもnew
が本当に必要であるという意味ではありません-動的に割り当てる必要のない型のセットは固定されており、既知です。オブジェクトの場合、コンパイラは、それがchar
の単純な値であるか、動的に割り当てる必要があるオブジェクトであるかを知ることができます(実際には知っています)。
Javaの設計の品質(または場合によってはその欠如)について、これが何を意味するかについては(まだ)意見を述べないようにします。
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
私は多くの答えが好きで、これを追加したいだけです:
コードを読みやすくします
このような状況が頻繁に発生するわけではありませんが、名詞と同じ名前を共有する動詞の名前のメソッドが必要だと考えてください。 C#およびその他の言語では、当然、Word object
が予約されています。しかし、そうでなければ、Foo = object()
はオブジェクトメソッド呼び出しの結果になるか、新しいオブジェクトをインスタンス化することになります。うまくいけば、new
キーワードのない言語はこの状況から保護されますが、コンストラクターを呼び出す前にnew
キーワードを要求することで、同じ名前のメソッドの存在を許可しますオブジェクト。
興味深いことに、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
_を使用することもできました。
多くのプログラミング言語には多くの痕跡があります。仕様によって存在する場合もあります(C++はCと可能な限り互換性があるように設計されています)。より悪質な例として、壊れたC switch
ステートメントがどこまで伝播したかを考えてみましょう。
JavascriptとC#について十分な知識がないので、Javaにnew
を使用する理由はC++にあること以外はわかりません。
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
を使用すると、Address
がAddress
メンバーではなくAddress
タイプであることが明確になります。これにより、メンバーはそのタイプと同じ名前を持つことができます。これがなければ、CAddress
のように、名前の衝突を回避するためにタイプの名前にプレフィックスを付ける必要があります。 Andersはハンガリーの表記法やそれに類似した表記法が好きではなく、C#を使わなくても使えるようにしたかったため、これは非常に意図的なものでした。それもおなじみの事実は二重のボーナスでした。
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
}
興味深いことに、完全な転送の出現により、C++では非配置new
もまったく必要ありません。したがって、おそらく、言及された言語のいずれかではもはや必要ありません。