Katakanlah saya sedang menulis metode yang harus mengembalikan Peta . Contohnya:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Setelah memikirkannya sebentar, saya memutuskan bahwa tidak ada alasan untuk memodifikasi Peta ini setelah dibuat. Jadi, saya ingin mengembalikan sebuah ImmutableMap .
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Haruskah saya membiarkan tipe pengembalian sebagai Peta generik, atau haruskah saya menentukan bahwa saya mengembalikan ImmutableMap?
Dari satu sisi, inilah tepatnya mengapa antarmuka dibuat untuk; untuk menyembunyikan detail implementasi.
Di sisi lain, jika saya membiarkannya seperti ini, pengembang lain mungkin melewatkan fakta bahwa objek ini tidak dapat diubah. Jadi, saya tidak akan mencapai tujuan utama dari objek yang tidak bisa diubah; untuk membuat kode lebih jelas dengan meminimalkan jumlah objek yang dapat berubah. Lebih buruk lagi, setelah beberapa saat, seseorang mungkin mencoba untuk mengubah objek ini, dan ini akan menghasilkan kesalahan waktu proses (Kompilator tidak akan memperingatkannya).
return ImmutableMap.of(somethingElse)
. Sebaliknya, Anda akan menyimpanImmutableMap.of(somethingElse)
sebagai bidang, dan hanya mengembalikan bidang ini. Semua ini memengaruhi desain di sini.Jawaban:
Jika Anda menulis API yang dihadapi publik dan keabadian itu merupakan aspek penting dari desain Anda, saya pasti akan membuatnya eksplisit baik dengan memiliki nama metode yang secara jelas menunjukkan bahwa peta yang dikembalikan tidak dapat diubah atau dengan mengembalikan jenis konkret dari peta. Menyebutnya di javadoc saja tidak cukup menurut saya.
Karena Anda tampaknya menggunakan implementasi Guava, saya melihat doc dan ini adalah kelas abstrak sehingga memberi Anda sedikit fleksibilitas pada tipe konkret yang sebenarnya.
Jika Anda menulis alat / pustaka internal, akan jauh lebih dapat diterima untuk hanya mengembalikan polos
Map
. Orang-orang akan tahu tentang bagian dalam kode yang mereka panggil atau setidaknya akan memiliki akses yang mudah ke sana.Kesimpulan saya adalah eksplisit itu baik, jangan biarkan hal-hal kebetulan.
sumber
Map
tipe antarmuka atauImmutableMap
tipe implementasi.ImmutableMap
, saran tim Guava adalah mempertimbangkanImmutableMap
antarmuka dengan jaminan semantik, dan Anda harus mengembalikan tipe tersebut secara langsung.Anda harus memiliki
ImmutableMap
tipe pengembalian Anda.Map
berisi metode yang tidak didukung oleh pelaksanaanImmutableMap
(misalnyaput
) dan ditandai@deprecated
diImmutableMap
.Menggunakan metode yang tidak berlaku lagi akan menghasilkan peringatan compiler & sebagian besar IDE akan memberi peringatan ketika orang mencoba menggunakan metode yang tidak digunakan lagi.
Peringatan lanjutan ini lebih disukai daripada memiliki pengecualian waktu proses sebagai petunjuk pertama Anda bahwa ada sesuatu yang salah.
sumber
Collections.immutableMap
itu?List
, tidak ada jaminan yangList::add
valid. CobaArrays.asList("a", "b").add("c");
. Tidak ada indikasi bahwa itu akan gagal saat runtime, namun gagal. Setidaknya Jambu biji memberi Anda perhatian.Anda harus menyebutkannya di javadocs. Pengembang memang membacanya, Anda tahu.
Tidak ada pengembang yang menerbitkan kodenya belum teruji. Dan ketika dia mengujinya, dia mendapatkan Exception yang tidak hanya melihat alasannya tetapi juga file dan baris tempat dia mencoba menulis ke peta yang tidak dapat diubah.
Namun perlu dicatat, hanya objek
Map
itu sendiri yang tidak dapat diubah, bukan objek yang dikandungnya.sumber
Itu benar, tetapi pengembang lain harus menguji kode mereka dan memastikan bahwa itu tercakup.
Namun demikian, Anda memiliki 2 opsi lagi untuk menyelesaikan ini:
Gunakan Javadoc
@return a immutable map
Pilih nama metode deskriptif
public Map<String, Integer> getImmutableMap() public Map<String, Integer> getUnmodifiableEntries()
Untuk kasus penggunaan konkret, Anda bahkan dapat memberi nama metode dengan lebih baik. Misalnya
public Map<String, Integer> getUnmodifiableCountByWords()
Apa lagi yang bisa kamu lakukan ?!
Anda dapat mengembalikan file
salinan
private Map<String, Integer> myMap; public Map<String, Integer> foo() { return new HashMap<String, Integer>(myMap); }
Pendekatan ini harus digunakan jika Anda berharap banyak klien akan memodifikasi peta dan selama peta hanya berisi sedikit entri.
CopyOnWriteMap
salinan pada koleksi tulis biasanya digunakan ketika Anda harus berurusan dengan
konkurensi. Tetapi konsep tersebut juga akan membantu Anda dalam situasi Anda, karena CopyOnWriteMap membuat salinan dari struktur data internal pada operasi mutatif (misalnya menambah, menghapus).
Dalam hal ini Anda memerlukan pembungkus tipis di sekitar peta Anda yang mendelegasikan semua pemanggilan metode ke peta yang mendasarinya, kecuali operasi mutatif. Jika operasi mutatif dipanggil, itu membuat salinan dari peta yang mendasarinya dan semua pemanggilan lebih lanjut akan didelegasikan ke salinan ini.
Pendekatan ini harus digunakan jika Anda mengharapkan bahwa beberapa klien akan memodifikasi peta.
Sayangnya java tidak memiliki file
CopyOnWriteMap
. Tetapi Anda mungkin menemukan pihak ketiga atau menerapkannya sendiri.Terakhir, Anda harus ingat bahwa elemen di peta Anda mungkin masih bisa berubah.
sumber
Pasti mengembalikan ImmutableMap, pembenarannya adalah:
sumber
Itu tergantung kelasnya sendiri. Guava's
ImmutableMap
tidak dimaksudkan untuk menjadi tampilan yang tidak dapat diubah ke dalam kelas yang bisa berubah. Jika kelas Anda tidak dapat diubah dan memiliki beberapa struktur yang pada dasarnya adalah anImmutableMap
, buatlah tipe yang dikembalikanImmutableMap
. Namun, jika kelas Anda bisa berubah, jangan. Jika Anda memiliki ini:public ImmutableMap<String, Integer> foo() { return ImmutableMap.copyOf(internalMap); }
Guava akan menyalin peta setiap saat. Itu lambat. Tapi kalau
internalMap
sudah jadiImmutableMap
, maka tidak apa-apa.Jika Anda tidak membatasi kelas untuk kembali
ImmutableMap
, Anda dapat kembaliCollections.unmodifiableMap
seperti ini:public Map<String, Integer> foo() { return Collections.unmodifiableMap(internalMap); }
Perhatikan bahwa ini adalah tampilan yang tidak dapat diubah ke dalam peta. Jika
internalMap
berubah, begitu juga salinan yang disimpan dalam cacheCollections.unmodifiableMap(internalMap)
. Saya masih lebih suka untuk getter.sumber
unmodifiableMap
hanya tidak dapat dimodifikasi oleh pemegang referensi - ini adalah tampilan dan pemegang peta pendukung dapat memodifikasi peta pendukung dan kemudian tampilan berubah. Jadi itu pertimbangan penting.Collections.unmodifiableMap
. Metode itu mengembalikan tampilan ke Peta asli daripada membuat objek peta baru yang terpisah. Jadi kode lain yang mereferensikan Peta asli dapat memodifikasinya, dan kemudian pemegang peta yang seharusnya tidak dapat dimodifikasi itu belajar dengan cara yang sulit bahwa peta memang dapat dimodifikasi dari bawahnya. Saya sendiri telah digigit oleh fakta itu. Saya belajar untuk mengembalikan salinan Peta atau menggunakan JambuImmutableMap
.Ini tidak menjawab pertanyaan persisnya, tetapi masih perlu dipertimbangkan apakah sebuah peta harus dikembalikan atau tidak. Jika peta tidak dapat diubah, maka metode utama yang akan disediakan didasarkan pada get (key):
public Integer fooOf(String key) { return map.get(key); }
Ini membuat API lebih ketat. Jika peta benar-benar diperlukan, ini dapat diserahkan kepada klien API dengan menyediakan aliran entri:
public Stream<Map.Entry<String, Integer>> foos() { map.entrySet().stream() }
Kemudian klien dapat membuat petanya sendiri yang tidak dapat diubah atau berubah sesuai kebutuhan, atau menambahkan entri ke petanya sendiri. Jika klien perlu mengetahui apakah nilainya ada, opsional dapat dikembalikan:
public Optional<Integer> fooOf(String key) { return Optional.ofNullable(map.get(key)); }
sumber
Peta Abadi adalah jenis Peta. Jadi meninggalkan jenis Map kembali tidak apa-apa.
Untuk memastikan bahwa pengguna tidak mengubah objek yang dikembalikan, metode dokumentasi dapat mendeskripsikan karakteristik objek yang dikembalikan.
sumber
Ini bisa dibilang masalah opini, tapi ide yang lebih baik di sini adalah menggunakan antarmuka untuk kelas peta. Antarmuka ini tidak perlu secara eksplisit mengatakan bahwa itu tidak dapat diubah, tetapi pesannya akan sama jika Anda tidak mengekspos metode penyetel apa pun dari kelas induk di antarmuka.
Simak artikel berikut ini:
andy gibson
sumber
Map
antarmuka adalah Anda tidak dapat meneruskannya ke fungsi API apa pun yang mengharapkan aMap
tanpa mentransmisikannya. Ini mencakup semua jenis konstruktor salinan dari jenis peta lainnya. Selain itu, jika mutabilitas hanya "disembunyikan" oleh antarmuka, pengguna Anda selalu dapat mengatasinya dengan mentransmisikan dan memecahkan kode Anda seperti itu.