Tip Java 99: Otomatiskan pembuatan toString ()

Pengembang yang mengerjakan proyek besar biasanya menghabiskan waktu berjam-jam menulis toStringmetode yang berguna . Meskipun setiap kelas tidak mendapatkan toStringmetodenya sendiri , setiap kelas penampung data akan melakukannya. Mengizinkan setiap pengembang untuk menulis toStringdengan caranya sendiri dapat menyebabkan kekacauan; Setiap pengembang pasti akan menghasilkan format yang unik. Akibatnya, menggunakan output selama debugging menjadi lebih sulit daripada yang diperlukan tanpa manfaat yang jelas. Oleh karena itu, setiap proyek harus melakukan standarisasi pada satu format untuk toStringmetode dan kemudian mengotomatiskan pembuatannya.

Otomatiskan toString

Sekarang saya akan mendemonstrasikan utilitas yang dapat Anda gunakan untuk melakukan hal itu. Alat ini secara otomatis menghasilkan alat yang teratur dan kuat

toString

metode untuk kelas tertentu, hampir menghilangkan waktu yang dihabiskan untuk mengembangkan metode. Ini juga memusatkan

toString()

format. Jika Anda mengubah format, Anda harus membuat ulang

toString

metode; Namun, ini masih jauh lebih mudah daripada mengubah ratusan atau ribuan kelas secara manual.

Mempertahankan kode yang dihasilkan juga mudah. Jika Anda menambahkan lebih banyak atribut di kelas, Anda mungkin juga diminta untuk membuat perubahan dalam toStringmetode. Karena pembuatan toStringmetode dilakukan secara otomatis, Anda hanya perlu menjalankan utilitas di kelas lagi untuk membuat perubahan. Ini lebih sederhana dan tidak terlalu rentan terhadap kesalahan daripada pendekatan manual.

Kode

Artikel ini tidak dimaksudkan untuk menjelaskan API Refleksi; kode berikut mengasumsikan bahwa Anda memiliki setidaknya pemahaman tentang konsep di balik Refleksi. Anda dapat mengunjungi

Sumber daya

bagian untuk dokumentasi Reflection API. Utilitasnya ditulis sebagai berikut:

package fareed.publications.utilities; import java.lang.reflect. *; kelas publik ToStringGenerator {public static void main (String [] args) {if (args.length == 0) {System.out.println ("Berikan nama kelas sebagai argumen baris perintah"); System.exit (0); } coba {Class targetClass = Class.forName (args [0]); if (! targetClass.isPrimitive () && targetClass! = String.class) {Bidang bidang [] = targetClass.getDeclaredFields (); Kelas cSuper = targetClass.getSuperclass (); // Mengambil keluaran kelas super ("StringBuffer buffer = new StringBuffer (500);"); // Konstruksi Buffer if (cSuper! = Null && cSuper! = Object.class) {output ("buffer.append (super.toString ());"); // Kelas super toString ()} untuk (int j = 0; j <field.length; j ++) {output ("buffer.append (\" "+ field [j].getName () + "= \"); "); // Tambahkan nama Bidang jika (bidang [j] .getType (). isPrimitive () || bidang [j] .getType () == String.class) // Periksa output primitif atau string ("buffer.append (this." + Fields [j] .getName () + ");"); // Tambahkan nilai kolom primitif else {/ * BUKAN kolom primitif jadi ini memerlukan pemeriksaan nilai NULL untuk objek agregat * / output ("if (this." + field [j] .getName () + "! = null)"); output ("buffer.append (this.") + bidang [j] .getName () + ".toString ());"); output ("else buffer.append (\" nilainya null \ ");");} // akhir dari else} // akhir dari for loop output ("return buffer.toString ();");}} catch (ClassNotFoundException e) {System.out.println ("Kelas tidak ditemukan di jalur kelas"); System.exit (0);}} keluaran ruang kosong statis pribadi (data String) {System.out.println (data); }}

Saluran keluaran kode

Format kode juga bergantung pada persyaratan alat proyek Anda. Beberapa pengembang mungkin lebih suka memiliki kode dalam file yang ditentukan pengguna di disk. Pengembang lain puas dengan

system.out

konsol, yang memungkinkan mereka untuk menyalin dan menyematkan kode di file sebenarnya secara manual. Saya serahkan saja opsi itu kepada Anda dan menggunakan metode paling sederhana:

system.out

pernyataan.

Keterbatasan pendekatan

Ada dua batasan penting untuk pendekatan ini. Yang pertama adalah tidak mendukung objek yang berisi siklus. Jika objek A berisi referensi ke objek B, yang kemudian berisi referensi ke objek A, alat ini tidak akan berfungsi. Namun, kasus itu jarang terjadi di banyak proyek.

Batasan kedua adalah bahwa menambah atau mengurangi variabel anggota membutuhkan regenerasi toStringmetode. Karena ini perlu dilakukan dengan atau tanpa alat, ini bukan masalah khusus untuk pendekatan ini.

Kesimpulan

Dalam artikel ini, saya telah menjelaskan utilitas otomatisasi kecil yang benar-benar dapat meningkatkan produktivitas pengembang dan memainkan peran kecil namun penting dalam pengurangan garis waktu proyek secara keseluruhan.


Kiat tindak lanjut

Setelah tip ini diterbitkan, saya menerima beberapa saran dari pembaca tentang cara meningkatkan kode. Dalam tindak lanjut ini, saya menjelaskan bagaimana saya telah memperbarui utilitas berdasarkan saran dan wawasan saya sendiri. Anda dapat menemukan kode sumber untuk peningkatan ini di Sumber Daya.

Perbaikan # 1, disarankan oleh Sangeeta Varma

Dalam kode asli saya, saya tidak menangani tipe array untuk objek dan tipe data primitif; kode baru sekarang menangani data array. Namun, kode hanya naik ke array dimensi tunggal dan tidak akan berfungsi untuk array beberapa dimensi. Saya belum dapat menemukan solusi umum untuk masalah ini karena, sejauh pengetahuan saya, tidak ada batasan jumlah dimensi untuk tipe data di Java (satu-satunya batasan adalah memori yang tersedia). Saya menyambut baik umpan balik yang dapat Anda tawarkan untuk sebuah solusi.

Perbaikan # 2, disarankan oleh Chris Sanscraint

Awalnya saya mengusulkan utilitas untuk waktu pengembangan dan bukan untuk lingkungan runtime. Mengizinkan utilitas untuk berjalan saat runtime bisa sangat berguna, tetapi mungkin memerlukan beberapa siklus CPU lagi. Namun, objek dumping / debugging (penggunaan dasar toString()) biasanya dilakukan selama waktu pengembangan, dan dimatikan untuk lingkungan produksi. Dalam beberapa kasus, penonaktifan ini di lingkungan produksi mungkin tidak dapat diterapkan karena beberapa proyek dapat digunakan toString()untuk tujuan logika bisnis. Saya menyarankan untuk membuat keputusan itu berdasarkan proyek per proyek.

Sebelum mengembangkan utilitas ini, saya sudah memiliki fleksibilitas waktu proses ini dalam pikiran saya. Pertama, saya mengembangkan kelas pendelegasian terpisah yang digunakan oleh setiap kelas klien untuk menghasilkan toString(). Kelas menghasilkannya menggunakan panggilan metode seperti return ToStringGenerator.generateToString(this), di mana thismenunjuk ke instance kelas klien saat ini dan pernyataan kode ditulis dalam toString()implementasi metode. Tetapi pendekatan itu gagal karena API Refleksi tidak memiliki kemampuan untuk mendapatkan nilai untuk anggota pribadi pada waktu proses. Jadi kelas itu hanya berguna untuk anggota masyarakat, yang tidak saya inginkan.

Tapi kemudian Mr. Sanscraint menunjukkan bahwa kode API Refleksi yang sama mendapatkan nilai anggota privat saat runtime ketika kode ditulis dalam metode kelas pemanggil yang sama. Jadi saya telah memperbarui utilitas untuk digunakan saat runtime, dan sebagai tambahan, toString()metode ini tidak perlu diperbarui atau diedit untuk pengurangan atau penambahan atribut apa pun di kelas target.

Perbaikan # 3, disarankan oleh Eric Ye

Awalnya saya menggunakan thisawalan untuk akses variabel anggota dalam kode yang dihasilkan, tetapi Tuan Ye menunjukkan bahwa kode juga dapat digunakan dalam metode statis atau bahkan untuk mengeluarkan anggota statis. Jadi kode yang diperbarui sekarang dapat menangani anggota kelas dan instance. Tn. Ye juga mengidentifikasi bug, yang telah diperbaiki dalam versi ini, yang menyebabkan kelas menghasilkan kode yang tidak berguna untuk kelas tanpa atribut.

Modifikasi kode

Setelah membuat utilitas runtime-enabled, saya frustrasi karena harus menyalin / menempel metode di setiap kelas, yang menjadi sulit karena kode baru terdiri dari beberapa metode.

Salah satu solusinya adalah membuat antarmuka / kelas dasar abstrak yang setidaknya akan menyelesaikan masalah tanda tangan metode, tetapi salin / tempel masih diperlukan. Solusi kelas dasar abstrak juga akan membatasi klien dari mengambil dari kelas lain.

Kelas dalam, bagaimanapun, memiliki kemampuan untuk mengakses anggota privat dari kelas induk sehingga kode refleksi, yang berjalan di dalam metodenya, juga bisa mendapatkan nilai privat. Jadi saya memutuskan untuk mengubah utilitas menjadi kelas dalam yang dapat dimasukkan ke dalam kelas klien induk. Saya juga menyediakan ToStringGeneratorExample.java yang menggunakan ToStringGenerator.java sebagai kelas dalam untuk mengimplementasikan toString()metode ini.

Akhirnya, saya ingin berterima kasih kepada orang-orang yang telah memberikan saran-saran mereka untuk memperbaiki pendekatan ini.

Syed Fareed Ahmad adalah seorang programmer, desainer, dan arsitek Java di Lahore, Pakistan. Dia terlibat dalam pengembangan solusi e-bisnis berbasis Java (Servlets, JSP, dan EJB), WebSphere-, dan XML.

Pelajari lebih lanjut tentang topik ini

  • Untuk kode sumber tindak lanjut

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/08/jw-javatip99.zip

  • Dokumentasi refleksi di Situs Sun

    //java.sun.com/products/jdk/1.1/docs/guide/reflection/index.html

  • Lihat semua Tips Java sebelumnya dan kirimkan Tips Anda sendiri

    //www.javaworld.com/javatips/jw-javatips.index.html

Artikel ini, "Java Tip 99: Automate toString () creation", awalnya diterbitkan oleh JavaWorld.