Nesneler arasında fark yoktur; Eğer bir var HashMap<String, Object>her iki durumda da. Arayüzde nesneye sahip olduğunuz bir fark var. İlk durumda, arayüz ise HashMap<String, Object>ikinci sırada Map<String, Object>. Ancak altta yatan nesne aynı.
Kullanmanın avantajı Map<String, Object>, altta yatan nesneyi, onu kullanan herhangi bir kodla sözleşmenizi bozmadan farklı türde bir harita olarak değiştirebilmenizdir. Bunu beyan ederseniz HashMap<String, Object>, temeldeki uygulamayı değiştirmek istiyorsanız sözleşmenizi değiştirmeniz gerekir.
Örnek: Diyelim ki bu sınıfı yazıyorum:
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...
}
Sınıf, alt sınıflarla paylaştığı (erişimci yöntemleri aracılığıyla) dize-> nesnesinin birkaç dahili haritasına sahiptir. Diyelim ki HashMapbaşlamak için s ile yazıyorum çünkü sınıf yazarken bu uygun yapı olduğunu düşünüyorum.
Daha sonra, Mary kodun alt sınıfını yazar. Her ikisiyle de yapması gereken bir şey var thingsve moreThingsdoğal olarak bunu ortak bir yöntemle koyuyor ve yöntemini tanımlarken getThings/ kullandığım aynı türü kullanıyor getMoreThings:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Daha sonra ben kullanırsanız aslında, daha iyi olduğuna karar TreeMapyerine HashMapde Foo. Ben güncelleme Foo, değişen HashMapiçin TreeMap. Şimdi, SpecialFooartık derlemiyor, çünkü sözleşmeyi çiğnedim: s Foosağladığını söylerdim HashMap, ama şimdi TreeMapsyerine veriyor . Bu yüzden SpecialFooşimdi düzeltmeliyiz (ve bu tür bir şey bir kod tabanından dalgalanabilir).
Uygulamamın bir HashMap(ve bu olduğu) kullandığını paylaşmak için gerçekten iyi bir nedenim olmadıkça , yapmam gereken şey beyan etmekti getThingsve bundan daha spesifik olmadan getMoreThingsgeri dönmekti Map<String, Object>. Aslında, hatta içinde, başka bir şey yapmak için iyi bir neden engelleme FooMuhtemelen ilan etmeli thingsve moreThingsolarak Mapdeğil, 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...
}
Şimdi Map<String, Object>yapabildiğim her yerde nasıl kullandığımı , yalnızca gerçek nesneleri oluşturduğumda özel olduğumu unutmayın.
Bunu yapsaydım, Mary bunu yapardı:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... ve değişen Fooolmazdı SpecialFoodurdurma derleme.
Arayüzler (ve temel sınıflar) , gerekli olduğu kadar değişiklik yapmak için esnekliğimizi kapakların altında tutarak , sadece gerektiği kadar açığa çıkarmamıza izin verir . Genel olarak, referanslarımızın mümkün olduğunca temel olmasını istiyoruz. Bunun a olduğunu bilmemize gerek yoksa HashMap, sadece a deyin Map.
Bu kör bir kural değildir, ancak genel olarak, en genel arayüze kodlama daha spesifik bir şeyi kodlamaktan daha az kırılgan olacaktır. Bunu hatırlasaydım, FooMary'yi başarısızlık için ayarlayan bir şey yaratmazdım SpecialFoo. Eğer Mary o zaman berbat halde olduğunu hatırlamıştı Foo, o ile onun özel yöntem ilan olurdu Mapyerine HashMapve benim değişen Foo'ın sözleşmesini onun kodunu etkiledi olmazdı.
Bazen bunu yapamazsın, bazen spesifik olmalısın. Ancak olmanız için bir nedeniniz yoksa, en az spesifik arayüze yöneliniz.