Pertimbangan toString () Java

Bahkan para pengembang Java pemula menyadari kegunaan metode Object.toString () yang tersedia untuk semua instance kelas Java dan dapat diganti untuk memberikan detail yang berguna mengenai instance tertentu dari kelas Java. Sayangnya, bahkan pengembang Java yang berpengalaman terkadang tidak memanfaatkan sepenuhnya fitur Java yang kuat ini karena berbagai alasan. Dalam posting blog ini, saya melihat Java yang sederhana toString()dan menjelaskan langkah-langkah mudah yang dapat diambil seseorang untuk meningkatkan utilitarianisme toString().

Implementasikan Secara Eksplisit (Timpa) toString ()

Mungkin pertimbangan paling penting terkait pencapaian nilai maksimum dari toString()adalah menyediakan implementasinya. Meskipun root dari semua hierarki kelas Java, Object, menyediakan implementasi toString () yang tersedia untuk semua kelas Java, perilaku default metode ini hampir tidak pernah berguna. Javadoc untuk Object.toString () menjelaskan apa yang disediakan secara default untuk toString () ketika versi kustom tidak disediakan untuk kelas:

Metode toString untuk kelas Objek mengembalikan string yang terdiri dari nama kelas di mana objek tersebut adalah turunan, karakter tanda-at `@ ', dan representasi heksadesimal unsigned dari kode hash objek. Dengan kata lain, metode ini mengembalikan string yang sama dengan nilai: getClass().getName() + '@' + Integer.toHexString(hashCode())

Sulit untuk menemukan situasi di mana nama kelas dan representasi heksadesimal dari kode hash objek yang dipisahkan oleh tanda @ berguna. Dalam hampir semua kasus, jauh lebih berguna untuk memberikan kustom, eksplisit

toString()

implementasi di kelas untuk mengganti versi default ini.

Javadoc untuk Object.toString () juga memberi tahu kita apa yang toString()seharusnya diperlukan oleh implementasi dan juga membuat rekomendasi yang sama seperti yang saya buat di sini: override toString():

Secara umum,  toString metode ini mengembalikan string yang "mewakili secara tekstual" objek ini. Hasilnya harus berupa representasi yang ringkas tetapi informatif yang mudah dibaca oleh seseorang. Direkomendasikan agar semua subclass mengganti metode ini.

Setiap kali saya menulis kelas baru, ada beberapa metode yang saya pertimbangkan untuk ditambahkan sebagai bagian dari tindakan pembuatan kelas baru. Ini termasuk

Kode hash()

dan

sama dengan (Objek)

jika pantas. Namun, menurut pengalaman saya dan menurut saya, penerapannya eksplisit

toString()

selalu sesuai.

Jika "rekomendasi" Javadoc bahwa "semua subclass menimpa metode ini" tidak cukup (maka saya tidak menganggap rekomendasi saya juga) untuk membenarkan kepada pengembang Java pentingnya dan nilai toString()metode eksplisit , maka saya sarankan untuk meninjau Josh Item Java Bloch yang Efektif "Always Override toString" untuk latar belakang tambahan tentang pentingnya penerapan toString(). Menurut pendapat saya, semua pengembang Java harus memiliki salinan Java Efektif , tetapi untungnya bab dengan item toString()ini tersedia bagi mereka yang tidak memiliki salinan: Metode Umum untuk Semua Objek.

Pertahankan / Perbarui toString ()

Ini menjengkelkan untuk secara eksplisit atau implisit memanggil objek toString()dalam pernyataan log atau alat diagnostik lainnya dan memiliki nama kelas default dan kode hash heksadesimal objek dikembalikan daripada sesuatu yang lebih berguna dan dapat dibaca. Ini hampir sama menjengkelkan untuk memiliki toString()implementasi yang tidak lengkap yang tidak menyertakan bagian penting dari karakteristik dan status objek saat ini. Saya mencoba untuk cukup disiplin dan menghasilkan dan mengikuti kebiasaan untuk selalu meninjau toString()implementasi bersama dengan meninjau equals(Object)dan hashCode()implementasi dari setiap kelas yang saya kerjakan yang baru bagi saya atau setiap kali saya menambahkan atau mengubah atribut kelas.

Hanya Fakta (Tapi Semua / Sebagian Besar!)

Dalam bab Java Efektifdisebutkan sebelumnya, Bloch menulis, "Ketika praktis, metode toString harus mengembalikan semua informasi menarik yang terkandung dalam objek." Mungkin menyakitkan dan membosankan untuk menambahkan semua atribut dari kelas yang berat-atribut ke implementasi toStringnya, tetapi nilai bagi mereka yang mencoba men-debug dan mendiagnosis masalah yang terkait dengan kelas itu akan sepadan dengan usahanya. Saya biasanya berusaha untuk memiliki semua atribut non-null yang signifikan dari contoh saya dalam representasi String yang dihasilkan (dan terkadang menyertakan fakta bahwa beberapa atribut adalah null). Saya juga biasanya menambahkan teks pengenal minimal untuk atribut. Dalam banyak hal, ini lebih merupakan seni daripada sains, tetapi saya mencoba menyertakan cukup teks untuk membedakan atribut tanpa menyalahkan pengembang masa depan dengan terlalu banyak detail. Hal terpenting bagi saya adalah mendapatkan atribut 'nilai-nilai dan beberapa jenis kunci pengenal di tempat.

Kenali Audiens-Mu

Salah satu kesalahan paling umum yang pernah saya lihat yang dilakukan oleh pengembang Java non-pemula toString()adalah melupakan apa dan untuk siapa toString()biasanya itu dimaksudkan. Secara umum, toString()adalah alat diagnostik dan debugging yang memudahkan untuk mencatat detail pada instance tertentu pada waktu tertentu untuk debugging dan diagnostik nanti. Biasanya merupakan kesalahan jika antarmuka pengguna menampilkan representasi String yang dihasilkan oleh toString()atau untuk membuat keputusan logika berdasarkan toString()representasi (sebenarnya, membuat keputusan logika pada String apa pun itu rapuh!). Saya telah melihat pengembang yang bermaksud baik telah toString()mengembalikan format XML untuk digunakan dalam beberapa aspek kode ramah XML lainnya. Kesalahan signifikan lainnya adalah memaksa klien untuk mengurai String yang dikembalikantoString()untuk mengakses anggota data secara terprogram. Mungkin lebih baik menyediakan metode pengambil / pengakses publik daripada mengandalkan metode yang toString()tidak pernah berubah. Semua ini adalah kesalahan karena pendekatan ini melupakan maksud dari sebuah toString()implementasi. Ini sangat berbahaya jika pengembang menghapus karakteristik penting dari toString()metode (lihat item terakhir) agar terlihat lebih baik pada antarmuka pengguna.

Saya suka toString()implementasi untuk memiliki semua detail yang relevan dan untuk memberikan pemformatan minimal agar detail ini lebih cocok. Pemformatan ini mungkin menyertakan karakter baris baru [ System.getProperty("line.seperator");] dan tab, titik dua, titik koma, dll. Saya tidak menginvestasikan jumlah waktu yang sama seperti yang saya lakukan pada hasil yang ditampilkan kepada pengguna akhir perangkat lunak, tetapi saya mencoba agar pemformatannya cukup bagus agar lebih mudah dibaca. Saya mencoba menerapkan toString()metode yang tidak terlalu rumit atau mahal untuk dipelihara, tetapi menyediakan beberapa pemformatan yang sangat sederhana. Saya mencoba memperlakukan pengelola kode saya di masa mendatang sebagaimana saya ingin diperlakukan oleh pengembang yang kodenya akan saya pelihara suatu hari nanti.

Dalam itemnya tentang toString()implementasi, Bloch menyatakan bahwa pengembang harus memilih apakah akan toString()mengembalikan format tertentu atau tidak . Jika format tertentu dimaksudkan, itu harus didokumentasikan dalam komentar Javadoc dan Bloch selanjutnya merekomendasikan bahwa penginisialisasi statis disediakan yang dapat mengembalikan objek ke karakteristik contoh berdasarkan String yang dihasilkan oleh toString(). Saya setuju dengan semua ini, tetapi saya yakin ini lebih merepotkan daripada kebanyakan pengembang yang bersedia melakukannya. Bloch juga menunjukkan bahwa setiap perubahan pada format ini di rilis mendatang akan menyebabkan rasa sakit dan kecemasan bagi orang-orang yang bergantung padanya (itulah mengapa menurut saya bukan ide yang baik untuk memiliki logika yang bergantung pada toString()output). Dengan disiplin yang signifikan untuk menulis dan memelihara dokumentasi yang sesuai, memiliki format yang telah ditentukan untuk atoString()mungkin masuk akal. Namun, sepertinya masalah bagi saya dan lebih baik hanya membuat metode baru dan terpisah untuk penggunaan seperti itu dan membiarkan yang toString()tidak terbebani.

Tidak Ada Efek Samping yang Ditoleransi

Betapapun pentingnya toString()penerapannya, secara umum tidak dapat diterima (dan tentu saja dianggap bentuk yang buruk) untuk memiliki panggilan eksplisit atau implisit dari toString()logika dampak atau menyebabkan pengecualian atau masalah logika. Penulis toString()metode harus berhati-hati untuk memastikan referensi diperiksa null sebelum mengaksesnya untuk menghindari NullPointerException. Banyak taktik yang saya jelaskan di pos Penanganan Java NullPointerException yang Efektif dapat digunakan dalam toString()implementasi. Misalnya, String.valueOf (Object) menyediakan mekanisme yang mudah untuk keamanan null pada atribut asal yang dipertanyakan.

Sama pentingnya bagi toString()pengembang untuk memeriksa ukuran larik dan ukuran koleksi lainnya sebelum mencoba mengakses elemen di luar koleksi itu. Secara khusus, terlalu mudah untuk menjalankan StringIndexOutOfBoundsException saat mencoba memanipulasi nilai String dengan String.substring.

Karena toString()implementasi objek dapat dengan mudah dipanggil tanpa disadari oleh pengembang, saran untuk memastikan objek tidak membuang pengecualian atau melakukan logika (terutama logika yang mengubah status) sangat penting. Hal terakhir yang diinginkan siapa pun adalah agar tindakan pencatatan status instance saat ini mengarah ke pengecualian atau perubahan status dan perilaku. Sebuah toString()implementasi harus secara efektif menjadi operasi read-only di mana negara objek dibaca untuk menghasilkan String untuk kembali. Jika ada atribut yang diubah dalam prosesnya, hal-hal buruk kemungkinan besar akan terjadi pada waktu yang tidak terduga.

Ini adalah posisi saya bahwa toString()implementasi hanya boleh menyertakan status dalam String yang dihasilkan yang dapat diakses dalam ruang proses yang sama pada saat pembuatannya. Bagi saya, tidak dapat dipertahankan untuk memiliki toString()layanan jarak jauh akses implementasi untuk membangun String sebuah instance. Mungkin sedikit kurang jelas adalah bahwa sebuah instance tidak boleh mengisi atribut data karena toString()dipanggil. The toString()implementasi hanya harus melaporkan tentang bagaimana hal-hal yang dalam contoh saat ini dan bukan pada bagaimana mereka mungkin atau akan di masa depan jika skenario yang berbeda tertentu terjadi atau jika hal-hal yang dimuat. Agar efektif dalam debugging dan diagnostik, toString()kebutuhan untuk menunjukkan bagaimana kondisi dan bukan bagaimana mereka bisa.

Pemformatan Sederhana Sangat Dihargai

Seperti dijelaskan di atas, penggunaan pemisah baris dan tab yang bijaksana dapat berguna dalam membuat contoh yang panjang dan kompleks lebih cocok saat dibuat dalam format String. Ada "trik" lain yang bisa membuat segalanya menjadi lebih baik. Tidak hanya String.valueOf(Object)memberikan beberapa nullperlindungan, tetapi juga hadir nullsebagai String "null" (yang sering merupakan representasi yang disukai dari null dalam String yang dihasilkan toString (). Arrays.toString (Object) berguna untuk merepresentasikan array dengan mudah sebagai String ( lihat posting saya Merangkai Array Java untuk detail tambahan).

Sertakan Nama Kelas dalam Representasi toString

Seperti dijelaskan di atas, implementasi default toString()menyediakan nama kelas sebagai bagian dari representasi instance. Jika kami menimpanya secara eksplisit, kami berpotensi kehilangan nama kelas ini. Biasanya ini bukan masalah besar jika membuat log string instance karena kerangka kerja logging akan menyertakan nama kelas. Namun, saya lebih suka berada di sisi yang aman dan selalu memiliki nama kelas yang tersedia. Saya tidak peduli tentang menjaga representasi kode hash heksadesimal dari default toString(), tetapi nama kelas dapat berguna. Contoh bagusnya adalah Throwable.toString (). Saya lebih suka menggunakan metode itu daripada getMessage atau getLocalizedMessage karena yang pertama ( toString()) tidak menyertakan Throwablenama kelas sementara dua metode terakhir tidak.

toString () Alternatif

Kami saat ini tidak memiliki ini (setidaknya bukan pendekatan standar), tetapi telah ada pembicaraan tentang kelas Objects di Java yang akan sangat membantu mempersiapkan representasi String dari berbagai objek, struktur data, dan koleksi dengan aman dan berguna. Saya belum mendengar kemajuan terbaru di JDK7 pada kelas ini. Kelas standar di JDK yang menyediakan representasi String dari objek bahkan ketika definisi kelas objek tidak menyediakan eksplisit toString()akan sangat membantu.

Apache Commons ToStringBuilder mungkin merupakan solusi paling populer untuk membangun implementasi toString () yang aman dengan beberapa kontrol pemformatan dasar. Saya telah membuat blog di ToStringBuilder sebelumnya dan ada banyak sumber daya online lainnya terkait penggunaan ToStringBuilder.

Tip Teknologi Java Glen McCluskey "Menulis Metode toString" memberikan detail tambahan tentang cara menulis metode toString () yang baik. Dalam salah satu komentar pembaca, Giovanni Pelosi menyatakan preferensi untuk mendelegasikan produksi representasi string dari sebuah instance dalam hierarki pewarisan dari kelas toString()ke kelas delegasi yang dibangun untuk tujuan itu.

Kesimpulan

Saya pikir sebagian besar pengembang Java mengakui nilai toString()implementasi yang baik . Sayangnya, penerapan ini tidak selalu sebaik atau berguna semampu mereka. Dalam posting ini saya telah mencoba menguraikan beberapa pertimbangan untuk meningkatkan toString()implementasi. Meskipun toString()metode tidak akan (atau setidaknya tidak boleh) memengaruhi logika seperti metode equals(Object)atau, hashCode()metode ini dapat meningkatkan efisiensi debugging dan diagnostik. Lebih sedikit waktu yang dihabiskan untuk mencari tahu apa keadaan objek berarti lebih banyak waktu untuk memperbaiki masalah, beralih ke tantangan yang lebih menarik, dan memenuhi lebih banyak kebutuhan klien.

Artikel ini, "Pertimbangan toString () Java" awalnya diterbitkan oleh JavaWorld.