私が作成した次のマップの違いは何ですか (別の質問では、人々はそれらを互換的に使用しているように回答しましたが、それらに違いがあるかどうか、またどのように異なるのか疑問に思っています)。
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
ベストアンサー1
オブジェクト間に違いはありません。どちらの場合も があります。オブジェクトに対するインターフェースHashMap<String, Object>
に違いがあります。最初のケースではインターフェースは ですが、2 番目のケースでは です。ただし、基礎となるオブジェクトは同じです。HashMap<String, Object>
Map<String, Object>
を使用する利点Map<String, Object>
は、それを使用しているコードとの契約を破ることなく、基礎となるオブジェクトを異なる種類のマップに変更できることです。 として宣言した場合HashMap<String, Object>
、基礎となる実装を変更するには契約を変更する必要があります。
例: このクラスを書いたとします:
class Foo {
private HashMap<String, Object> things;
private HashMap<String, Object> moreThings;
protected HashMap<String, Object> getThings() {
return this.things;
}
protected HashMap<String, Object> getMoreThings() {
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
クラスには、string->object の内部マップがいくつかあり、サブクラスと (アクセサ メソッドを介して) 共有します。HashMap
クラスを記述するときに使用する適切な構造だと思うので、まずは s を使用して記述することにします。
things
その後、Mary はそれをサブクラス化するコードを書きます。との両方で実行する必要があるためmoreThings
、当然、それを共通メソッドに配置し、メソッドを定義するときにgetThings
/で使用したのと同じ型を使用しますgetMoreThings
。
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
その後、実際にはではTreeMap
ではなくを使用する方が良いと判断しました。 を更新してに変更します。これで はコンパイルされなくなりました。これは規約に違反しているためです。以前は が提供されていましたが、今はではなく が提供されています。そのため、今すぐ修正する必要があります(このようなことはコードベース全体に波及する可能性があります)。HashMap
Foo
Foo
HashMap
TreeMap
SpecialFoo
Foo
HashMap
TreeMaps
SpecialFoo
私の実装が を使用していることを共有する十分な理由がない限りHashMap
(実際にそのようなことは起こります)、私がすべきことは、それ以上具体的にせず、 と を を返すものとして宣言することでしたgetThings
。getMoreThings
実際Map<String, Object>
、何か他のことをする十分な理由がない限り、 内でも、とを/ではなくとしてFoo
宣言するべきでしょう。things
moreThings
Map
HashMap
TreeMap
class Foo {
private Map<String, Object> things; // <== Changed
private Map<String, Object> moreThings; // <== Changed
protected Map<String, Object> getThings() { // <== Changed
return this.things;
}
protected Map<String, Object> getMoreThings() { // <== Changed
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
Map<String, Object>
実際のオブジェクトを作成するときにのみ具体的に指定し、可能な場合はどこでも使用していることに注意してください。
もし私がそうしていたら、メアリーはこうしていたでしょう。
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
...変更してもコンパイルはFoo
停止されませんSpecialFoo
。
インターフェース (および基本クラス) を使用すると、必要な情報だけを公開し、裏で適切な変更を加えるための柔軟性を維持できます。一般に、参照はできるだけ基本的なものにします。 であることを知る必要がない場合はHashMap
、 と呼ぶだけですMap
。
これは盲目的なルールではありませんが、一般的に、最も一般的なインターフェースへのコーディングは、より具体的なものへのコーディングよりも脆弱性が低くなります。私がそれを覚えていれば、Foo
で Mary を失敗させる を作成しなかったでしょうSpecialFoo
。Maryがそれを覚えていれば、私が を間違えたとしても、彼女はの代わりにFoo
でプライベート メソッドを宣言し、私が の契約を変更しても彼女のコードには影響がなかったでしょう。Map
HashMap
Foo
それができないときもあれば、具体的にしなければならないときもあります。しかし、特別な理由がない限り、できるだけ具体的でないインターフェースを選んでください。