Pertimbangan Java toString ()

Bahkan para pemaju Java awal mengetahui kegunaan metode Object.toString () yang tersedia untuk semua contoh kelas Java dan dapat diganti untuk memberikan perincian berguna mengenai contoh tertentu dari kelas Java. Sayangnya, walaupun pemaju Java yang berpengalaman kadang-kadang tidak memanfaatkan sepenuhnya fitur Java yang kuat ini dengan pelbagai alasan. Dalam catatan blog ini, saya melihat Java yang rendah hati toString()dan menerangkan langkah-langkah mudah yang boleh diambil untuk meningkatkan utilitarianisme toString().

Laksanakan secara Eksplisit (Ganti) ke String ()

Mungkin pertimbangan yang paling penting berkaitan dengan mencapai nilai maksimum dari toString()memberikan pelaksanaannya. Walaupun akar semua hierarki kelas Java, Object, menyediakan implementasi toString () yang tersedia untuk semua kelas Java, tingkah laku lalai kaedah ini hampir tidak pernah berguna. Javadoc untuk Object.toString () menjelaskan apa yang disediakan secara lalai untuk toString () apabila versi khusus tidak disediakan untuk kelas:

Kaedah toString untuk kelas Objek mengembalikan rentetan yang terdiri dari nama kelas yang objeknya adalah contoh, watak at-sign `@ ', dan perwakilan heksadesimal tanpa tanda dari kode hash objek. Dengan kata lain, kaedah ini mengembalikan rentetan yang sama dengan nilai: getClass().getName() + '@' + Integer.toHexString(hashCode())

Sukar untuk menghasilkan situasi di mana nama kelas dan perwakilan heksadesimal kod hash objek yang dipisahkan oleh tanda @ berguna. Dalam hampir semua kes, jauh lebih berguna untuk memberikan kebiasaan, eksplisit

toString()

pelaksanaan dalam kelas untuk mengatasi versi lalai ini.

Javadoc for Object.toString () juga memberitahu kita apa yang toString()biasanya dilaksanakan oleh pelaksanaan dan juga membuat cadangan yang sama yang saya buat di sini: ganti toString():

Secara umum,  toString kaedah mengembalikan rentetan yang "mewakili teks" objek ini. Hasilnya mestilah gambaran ringkas tetapi bermaklumat yang mudah dibaca oleh seseorang. Sebaiknya semua subkelas mengatasi kaedah ini.

Setiap kali saya menulis kelas baru, ada beberapa kaedah yang saya pertimbangkan untuk menambahkan sebagai sebahagian daripada tindakan penciptaan kelas baru. Ini termasuk

hashCode ()

dan

sama dengan (Objek)

sekiranya sesuai. Namun, berdasarkan pengalaman dan pendapat saya, melaksanakan secara eksplisit

toString()

sentiasa sesuai.

Sekiranya "cadangan" Javadoc bahawa "semua subkelas mengatasi kaedah ini" tidak mencukupi (maka saya tidak menganggap bahawa cadangan saya adalah sama ada) untuk membenarkan kepada pemaju Java mengenai kepentingan dan nilai toString()kaedah eksplisit , maka saya cadangkan untuk mengkaji semula Josh Item Java Berkesan Bloch "Selalu Override toString" untuk latar belakang tambahan mengenai pentingnya pelaksanaan toString(). Menurut pendapat saya, semua pengembang Java harus memiliki salinan Java yang Berkesan , tetapi untungnya bab dengan item toString()ini tersedia bagi mereka yang tidak memiliki salinan: Metode Umum untuk Semua Objek.

Kekalkan / Kemas kini ke String ()

Mengecewakan secara eksplisit atau tersirat memanggil objek toString()dalam penyataan log atau alat diagnostik lain dan mengembalikan nama kelas lalai dan kod hash heksadesimal objek dikembalikan daripada sesuatu yang lebih berguna dan boleh dibaca. Hampir mengecewakan apabila toString()pelaksanaannya tidak lengkap yang tidak termasuk ciri-ciri dan keadaan semasa objek yang ketara. Saya berusaha untuk cukup berdisiplin dan menjana serta mengikuti kebiasaan untuk selalu mengkaji toString()pelaksanaannya dan juga mengkaji equals(Object)dan hashCode()pelaksanaan mana-mana kelas yang saya kerjakan yang baru bagi saya atau setiap kali saya menambah atau mengubah sifat kelas.

Hanya Fakta (Tetapi Semua / Sebilangannya!)

Dalam bab Java Berkesansebelumnya disebutkan, Bloch menulis, "Apabila praktikal, kaedah toString harus mengembalikan semua maklumat menarik yang terdapat dalam objek." Sungguh menyakitkan dan membosankan untuk menambahkan semua atribut kelas yang mempunyai atribut ke pelaksanaannya ke String, tetapi nilai bagi mereka yang cuba men-debug dan mendiagnosis masalah yang berkaitan dengan kelas itu akan bernilai. Saya biasanya berusaha untuk mempunyai semua atribut bukan nol yang signifikan dari contoh saya dalam perwakilan String yang dihasilkan (dan kadang-kadang merangkumi kenyataan bahawa beberapa atribut adalah nol). Saya juga biasanya menambahkan teks pengenalan minimum untuk atribut. Ini dalam banyak hal lebih merupakan seni daripada sains, tetapi saya cuba memasukkan teks yang mencukupi untuk membezakan atribut tanpa memalsukan pembangun masa depan dengan terlalu banyak perincian. Perkara yang paling penting bagi saya adalah mendapatkan atribut 'nilai dan beberapa jenis kunci pengenalan yang ada.

Ketahui Penonton Anda

Salah satu kesalahan yang paling biasa saya lihat mengenai pembangun Java yang bukan pemula toString()adalah melupakan apa dan yang toString()biasanya dimaksudkan. Secara umum, toString()adalah alat diagnostik dan penyahpepijatan yang memudahkan log maklumat pada contoh tertentu pada waktu tertentu untuk penyahpepijatan dan diagnostik kemudian. Lazimnya adalah suatu kesalahan jika antara muka pengguna menampilkan representasi String yang dihasilkan oleh toString()atau membuat keputusan logik berdasarkan toString()representasi (sebenarnya, membuat keputusan logik pada String mana pun rapuh!) Saya telah melihat pemaju yang bermaksud baik telah toString()mengembalikan format XML untuk digunakan dalam beberapa aspek kod yang mesra XML yang lain. Kesalahan penting lain adalah memaksa klien untuk menguraikan String yang dikembalikantoString()untuk mengakses ahli data secara program. Mungkin lebih baik menyediakan kaedah mendapatkan / aksesor awam daripada bergantung pada yang toString()tidak pernah berubah. Semua ini adalah kesilapan kerana pendekatan ini melupakan niat toString()pelaksanaan. Ini sangat berbahaya jika pembangun membuang ciri penting dari toString()kaedah (lihat item terakhir) untuk menjadikannya kelihatan lebih baik di antara muka pengguna.

Saya suka toString()pelaksanaannya mempunyai semua perincian yang berkaitan dan memberikan format minimum untuk menjadikan perincian ini lebih enak. Pemformatan ini mungkin merangkumi watak baris baru yang dipilih dengan bijak [ System.getProperty("line.seperator");] dan tab, titik dua, titik koma, dan lain-lain. Saya tidak melaburkan jumlah masa yang sama seperti yang saya lakukan pada hasil yang disampaikan kepada pengguna akhir perisian, tetapi saya mencuba untuk menjadikan formatnya cukup bagus agar lebih mudah dibaca Saya cuba menerapkan toString()kaedah yang tidak terlalu rumit atau mahal untuk dijaga, tetapi menyediakan beberapa format yang sangat mudah. Saya cuba melayan penyelenggara kod saya yang akan datang kerana saya ingin dilayan oleh pemaju yang kodnya akan saya simpan suatu hari nanti.

Dalam itemnya mengenai toString()pelaksanaan, Bloch menyatakan bahawa pemaju harus memilih sama ada mahu mendapatkan toString()kembali format tertentu atau tidak . Sekiranya format tertentu dimaksudkan, itu harus didokumentasikan dalam komen Javadoc dan Bloch lebih lanjut mengesyorkan agar penginisialisasi statis disediakan yang dapat mengembalikan objek ke ciri contohnya berdasarkan String yang dihasilkan oleh toString(). Saya setuju dengan semua ini, tetapi saya percaya ini lebih menyusahkan daripada kebanyakan pemaju yang ingin pergi. Bloch juga menunjukkan bahawa apa-apa perubahan pada format ini dalam siaran masa depan akan menyebabkan rasa sakit dan marah bagi orang yang bergantung padanya (itulah sebabnya saya tidak fikir idea yang baik untuk mempunyai logik bergantung pada toString()output). Dengan disiplin yang signifikan untuk menulis dan mengekalkan dokumentasi yang sesuai, mempunyai format yang telah ditentukan untuk atoString()mungkin masuk akal. Walau bagaimanapun, nampaknya menyusahkan saya dan lebih baik membuat kaedah baru dan berasingan untuk kegunaan tersebut dan membiarkan yang toString()tidak dibebankan.

Tiada kesan sampingan yang ditoleransi

Sama pentingnya dengan toString()pelaksanaannya, umumnya tidak dapat diterima (dan tentunya dianggap sebagai bentuk buruk) untuk mempunyai panggilan toString()logik impak yang eksplisit atau tersirat atau membawa kepada pengecualian atau masalah logik. Pengarang toString()kaedah harus berhati-hati untuk memastikan bahawa rujukan diperiksa untuk nol sebelum mengaksesnya untuk mengelakkan NullPointerException. Banyak taktik yang saya jelaskan dalam posting Java NullPointerException Java yang berkesan dapat digunakan dalam toString()pelaksanaannya. Sebagai contoh, String.valueOf (Objek) menyediakan mekanisme mudah untuk keselamatan nol pada atribut asal yang dipersoalkan.

Sama pentingnya bagi toString()pembangun untuk memeriksa ukuran array dan ukuran koleksi lain sebelum cuba mengakses elemen di luar koleksi tersebut. Khususnya, terlalu mudah untuk menghadapi StringIndexOutOfBoundsException ketika cuba memanipulasi nilai String dengan String.substring.

Oleh kerana toString()pelaksanaan objek dapat dengan mudah dipanggil tanpa pengembang menyedarinya dengan teliti, nasihat ini untuk memastikannya tidak membuang pengecualian atau melakukan logik (terutama logik yang mengubah keadaan) sangat penting. Perkara terakhir yang diingini oleh seseorang ialah melakukan tindakan mencatat keadaan semasa yang membawa kepada pengecualian atau perubahan keadaan dan tingkah laku. A toString()pelaksanaan berkesan harus operasi baca sahaja di mana kerajaan objek dibaca untuk menjana String pergi-balik. Sekiranya ada atribut yang diubah dalam proses, perkara buruk mungkin berlaku pada masa yang tidak dapat diramalkan.

Ini adalah pendapat saya bahawa toString()pelaksanaan hanya harus memasukkan keadaan dalam String yang dihasilkan yang dapat diakses di ruang proses yang sama pada saat penjanaannya. Bagi saya, tidak dapat dipertahankan untuk memiliki perkhidmatan toString()akses akses jarak jauh untuk membangun String instance. Mungkin sedikit kurang jelas adalah bahawa contoh tidak boleh mengisi atribut data kerana toString()dipanggil. The toString()pelaksanaan hanya perlu melaporkan tentang bagaimana perkara-perkara yang dalam contoh semasa dan bukan bagaimana mereka mungkin atau dalam masa akan datang jika senario tertentu berlaku atau jika perkara-perkara yang dimuatkan. Untuk berkesan dalam proses penyahpepijatan dan diagnostik, perlunya toString()menunjukkan bagaimana keadaannya dan bukan bagaimana keadaannya.

Pemformatan Ringkas Dihargai dengan Hormat

Seperti yang dijelaskan di atas, penggunaan pemisah baris dan tab yang bijaksana dapat berguna dalam membuat contoh yang panjang dan kompleks lebih enak ketika dihasilkan dalam format String. Terdapat "muslihat" lain yang boleh menjadikan keadaan lebih baik. Bukan sahaja String.valueOf(Object)memberikan beberapa nullperlindungan, tetapi juga ditampilkan nullsebagai String "null" (yang sering menjadi representasi null yang disukai dalam String () - dihasilkan String. Arrays.toString (Object) berguna untuk dengan mudah mewakili tatasusunan sebagai Strings ( lihat catatan saya Stringifying Java Arrays untuk maklumat tambahan).

Sertakan Nama Kelas dalam Perwakilan toString

Seperti yang dijelaskan di atas, pelaksanaan default toString()memberikan nama kelas sebagai bagian dari representasi instance. Apabila kami secara eksplisit mengatasi perkara ini, kami berpotensi kehilangan nama kelas ini. Ini bukan masalah besar biasanya jika mencatat rentetan instance kerana kerangka kerja log akan merangkumi nama kelas. Walau bagaimanapun, saya lebih suka berada di pihak yang selamat dan selalu mempunyai nama kelas. Saya tidak peduli untuk menjaga perwakilan kod hash heksadesimal dari lalai toString(), tetapi nama kelas boleh berguna. Contoh yang baik ialah Throwable.toString (). Saya lebih suka menggunakan kaedah itu daripada getMessage atau getLocalizedMessage kerana yang pertama ( toString()) termasuk Throwablenama kelas sementara dua kaedah terakhir tidak.

toString () Alternatif

Saat ini kami tidak memiliki ini (setidaknya bukan pendekatan standar), tetapi telah ada pembicaraan mengenai kelas Objek di Jawa yang akan berlangsung lama dengan selamat dan berguna menyiapkan String representasi dari berbagai objek, struktur data, dan koleksi. Saya belum pernah mengetahui perkembangan terkini dalam JDK7 di kelas ini. Kelas standard dalam JDK yang memberikan representasi String objek walaupun definisi kelas objek tidak memberikan eksplisit toString()akan sangat membantu.

Apache Commons ToStringBuilder mungkin merupakan penyelesaian paling popular untuk membina implementasi toString () yang selamat dengan beberapa kawalan pemformatan asas. Saya pernah menulis blog di ToStringBuilder sebelum ini dan terdapat banyak sumber dalam talian lain mengenai penggunaan ToStringBuilder.

Petua Teknologi Teknologi Glen McCluskey's Java "Kaedah Menulis toString" memberikan perincian tambahan mengenai cara menulis kaedah toString () yang baik. Dalam salah satu komen pembaca, Giovanni Pelosi menyatakan keutamaan untuk mewakilkan produksi representasi rentetan contoh dalam hierarki warisan dari toString()kelas perwakilan yang dibina untuk tujuan tersebut.

Kesimpulannya

Saya rasa kebanyakan pembangun Java mengakui nilai toString()pelaksanaan yang baik . Sayangnya, pelaksanaan ini tidak semestinya baik atau bermanfaat sebagaimana mestinya. Dalam catatan ini saya telah berusaha untuk menggariskan beberapa pertimbangan untuk meningkatkan toString()pelaksanaan. Walaupun toString()kaedah tidak akan (atau paling tidak seharusnya) mempengaruhi logik seperti equals(Object)atau hashCode()kaedah boleh, ia dapat meningkatkan debugging dan kecekapan diagnostik. Lebih sedikit masa yang dihabiskan untuk mengetahui keadaan objek bermaksud lebih banyak masa untuk menyelesaikan masalah, beralih ke cabaran yang lebih menarik, dan memuaskan lebih banyak keperluan pelanggan.

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