私は過去6か月ほどの間にC#を学び、現在Javaについて掘り下げています。私の質問は、インスタンスの作成(実際にはどちらの言語でも)に関するもので、それは次のようなものです。この例を取る
_Person Bob = new Person();
_
オブジェクトが2回指定されている理由はありますか? something_else Bob = new Person()
はありますか?
私が慣習に従っているなら、それはもっと似ているでしょう:
_int XIsAnInt;
Person BobIsAPerson;
_
またはおそらくこれらの1つ:
_Person() Bob;
new Person Bob;
new Person() Bob;
Bob = new Person();
_
「それがまさにそれが行われている方法」よりも良い答えがあるかどうか私は興味があると思います。
Something_else Bob = new Person()はありますか?
はい、相続のためです。次の場合:
public class StackExchangeMember : Person {}
次に:
Person bob = new StackExchangeMember();
Person sam = new Person();
ボブも人であり、おかしなことに、彼は他の誰とも違った扱いを受けたくありません。
さらに、ボブに超大国を与えることができます:
public interface IModerator { }
public class StackOverFlowModerator : StackExchangeMember, IModerator {}
IModerator bob = new StackOverFlowModerator();
ですから、彼は他のモデレーターとは違った扱いを受けることを許しません。そして、彼はフォーラムをこっそり見て、シークレットモードで全員に連絡を取り続けることを好みます。
StackExchangeMember bob = new StackOverFlowModerator();
それから彼はいくつかの貧弱な最初のポスターを見つけたとき、彼は彼の不可視性のマントを脱ぎ捨てて激突します。
((StackOverFlowModerator) bob).Smite(sam);
その後、彼はすべての無実の行動をとることができます。
((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();
コードの最初の行を見てみましょう。
_Person Bob = new Person();
_
最初のPerson
は型指定です。C#では、次のように指定するだけでこれを省略できます。
_var Bob = new Person();
_
コンパイラーは、コンストラクター呼び出しPerson()
から変数Bobの型を推論します。
しかし、あなたはこのようなものを書きたいかもしれません:
_IPerson Bob = new Person();
_
PersonのAPI契約全体ではなく、インターフェースIPerson
で指定された契約のみを満たしている場合。
この構文はC++のレガシーであり、ちなみに次の両方を備えています。
_Person Bob;
_
そして
_Person *bob = new Bob();
_
1つ目は現在のスコープ内にオブジェクトを作成し、2つ目は動的オブジェクトへのポインタを作成します。
あなたは間違いなくsomething_else Bob = new Person()
を持つことができます
_IEnumerable<int> nums = new List<int>(){1,2,3,4}
_
ここでは2つの異なることを行っています。ローカル変数nums
のタイプを示し、タイプ 'List'の新しいオブジェクトを作成してそこに配置するとします。
C# ほとんどの場合、変数の型はユーザーが入力したものと同じであるため、ある程度同意します。
_var nums = new List<int>();
_
一部の言語では、変数の型を F# のように宣言しないように最善を尽くします。
_let list123 = [ 1; 2; 3 ]
_
_int x
_と_Person bob
_の間には大きな違いがあります。 int
はint
はint
であり、常にint
である必要があり、int
以外にすることはできません。 int
を宣言するときに初期化しない場合でも(_int x;
_)、デフォルト値に設定されたint
のままです。
ただし、_Person bob
_を宣言する場合、bob
という名前がいつ実際に何を参照するかについては、かなりの柔軟性があります。 Person
を参照することも、他のクラスを参照することもできます。 Programmer
、Person
から派生;オブジェクトをまったく参照しないnull
の場合もあります。
例えば:
_ Person bob = null;
Person carol = new Person();
Person ted = new Programmer();
Person alice = personFactory.functionThatReturnsSomeKindOfPersonOrNull();
_
言語設計者は確かに、より少ないシンボルでPerson carol = new Person()
と同じことを達成する代替構文を作成できたかもしれませんが、それでもPerson carol = new Person()
を許可する必要がありました(またはいくつかの奇妙なルールを作成しました)上記の4つの例のうちの特定の1つを違法にする)。彼らは、非常に簡潔なコードを書くよりも、言語を「単純」に保つことにもっと関心を持っていました。これは、より短い代替構文を提供しないという彼らの決定に影響を与えた可能性がありますが、いずれにしても、それは必要ではなく、提供しませんでした。
2つの宣言は異なる場合がありますが、多くの場合同じです。 Java=の一般的な推奨パターンは次のようになります:
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
これらの変数list
およびmap
は、コードが特定の実装をインスタンス化するときに、インターフェースList
およびMap
を使用して宣言されます。このように、残りのコードはTreeMap
インターフェースの外部にあるHashMap
APIのどの部分にも依存できないため、残りのコードはインターフェースにのみ依存し、Map
のようにインスタンス化する別の実装クラスを選択するのは簡単です。 。
2つのタイプが異なるもう1つの例は、インスタンス化する特定のサブクラスを選択し、それを基本タイプとして返すファクトリメソッドにあります。これにより、呼び出し側は実装の詳細、たとえば「ポリシー」の選択を意識する必要がなくなります。
型推論により、ソースコードの冗長性を修正できます。例:Java
List<String> listOne = Collections.emptyList();
型推論と宣言のおかげで適切な種類のリストを構築します
static <T> List<T> emptyList();
一部の言語では、型推論はさらに進んでいます(C++など)。
auto p = new Person();
素人の言葉で:
var = new Process()
を実行するだけで、最初に変数を宣言するわけではありません。何が起こっているかを制御するレベルについてもです。オブジェクト/変数の宣言が自動的にコンストラクターを呼び出す場合、たとえば、
Person somePerson;
自動的にと同じでした
Person somePerson = new Person(blah, blah..);
その場合、(たとえば)静的ファクトリメソッドを使用して、デフォルトのコンストラクタではなくオブジェクトをインスタンス化することはできません。つまり、新しいオブジェクトインスタンスのコンストラクタを呼び出したくない場合があります。
この例は Joshua Bloch のEffective Javaで説明されています(皮肉にもアイテム1です!)