Last updated on January 3, 2020
Her ne kadar java.lang.Object
bir toString()
gerçekleştirimi sunsa da, geri döndürdüğü karakter dizisi genellikle sizin sınıfınızı kullanmak isteyen bir yazılımcının görmek istediği şey değildir. Bu değer sınıfın adı, ardından gelen ‘@’ karakteri ve nesnenin hash kodunun onaltılık sistemde (hexadecimal) ifadesini içermektedir, örneğin “PhoneNumber@163b91”. toString
sözleşmesi, bu metottan döndürülen değerin “kolay okunup anlaşılabilen, kısa ama bilgi verici” olması gerektiğini söyler. “PhoneNumber@163b91” değerinin kısa ve okuması kolay olduğunu savunabilirsiniz ancak “(707) 867-5309” ile karşılaştırıldığında bilgi verici olmadığı açıktır. toString
sözleşmesi bunların haricinde “Bu metodu bütün alt sınıfların geçersiz kılması tavsiye edilir” demektedir. Kesinlikle uyulması gereken bir tavsiye!
Her ne kadar equals
(Madde 10) ve hashCode
(Madde 11) sözleşmeleri kadar kritik olmasa da, güzel bir toString gerçekleştirimi sağlamak sınıfınızı çok daha kolay kullanılabilir hale getirir. toString metodu, bir nesne printf, println gibi metotlara parametre olarak geçildiğinde, karakter dizilerine eklendiğinde veya assert ile birlikte kullanıldığında otomatik olarak işletilir.
PhoneNumber sınıfı için iyi bir toString
metodu tanımladıysanız, yararlı debug mesajları yazdırmak aşağıdaki gibi son derece kolay olacaktır.
System.out.println("Failed to connect: " + phoneNumber);
Sınıfınızı kullanan programcılar bunun gibi debug mesajlarını siz bir toString metodu sağlasanız da sağlamasanız da yazdıracaklardır, ancak sağlamazsanız görecekleri mesaj çok da faydalı olmayacaktır. İyi bir toString metodu tanımlamanın bir başka faydası da sınıfınız bir veri yapısı içerisinde kullanıldığında ortaya çıkar. Bir HashMap yazdırdığınızda “{Jenny=PhoneNumber@163b91}” mi yoksa “{Jenny=(707) 867-5309}” mi görmek isterdiniz?
Elverişli durumlarda toString metodu nesne içerisindeki bütün önemli alanları içeren bir karakter dizisi döndürmelidir. Yukarıdaki PhoneNumber sınıfı buna bir örnek olabilir. Eğer nesne çok fazla alan içeriyorsa veya içerdiği alanlar karakter dizisi olarak ifade edilemiyorsa, o zaman “Manhattan white pages (1487536 listings)” gibi makul bir özet karakter dizisi döndürülebilir.
toString
metodunu geçersiz kılarken vermeniz gereken önemli kararlardan birisi de döndürdüğünüz karakter dizisinin formatını belirleyip belirlememektir. PhoneNumber gibi değer sınıfları için bunu yapmanız tavsiye edilir. Format belirlemenin avantajı, nesnenizi standart, net ve okunabilir bir karakter dizisi olarak ifade edebilmektir. Bu ifade şekli girdi/çıktı olarak kullanılabilir veya CSV gibi dosyalar içerisinde saklanabilir. Eğer bir format tanımlarsanız, bunun yanında uyumlu bir statik fabrika metodu veya yapıcı metot tanımlamak da mantıklı olacaktır. Böylece programcılar nesnenin kendisi ve karakter dizisi ifadesi arasında kolayca geçiş yapabilirler. Bu yaklaşım BigInteger, BigDecimal gibi birçok değer sınıfı için Java kütüphanesi içerisinde kullanılmıştır.
toString
için bir format belirlemenin dezavantajı ise, eğer sınıfınız yaygın olarak kullanılıyorsa belirlediğiniz formatı gelecekte değiştirme şansınızın kalmamasıdır. Programcılar sizin formatınıza uygun olarak karakter dizisi üzerinde işlem yapan kodlar yazacaklardır ve siz formatı değiştirdiğiniz zaman bu kodlar çalışmayacaktır. Bu da sınıfınızı kullanan programcıları memnun etmeyecektir. Bir format belirlemediğiniz zaman gelecek sürümlerde değişiklik yapma esnekliğini korumuş olursunuz.
Bir format belirleseniz de belirlemeseniz de, niyetinizi açık bir biçimde belirtmelisiniz. Format belirlediyseniz de net bir biçimde anlatmalısınız. Mesela Madde 11’de yazdığımız PhoneNumber
sınıfı için aşağıdaki gibi bir toString
olabilir:
/**
* Telefon numarası nesnesinin karakter dizisi olarak ifadesini döndürür.
* Bu karakter dizisi 14 karakterden oluşur ve formatı şu şekildedir
* "(XXX) YYY-ZZZZ", burada XXX alan kodu, YYY ön ek ve ZZZZ hat numarasıdır.
* (Her bir büyük harf bir rakamı temsil etmektedir)
*
* Eğer bu telefon numarasının üç kısmından herhangi birisi o alanı
* dolduracak kadar büyük değilse, o zaman başına yeteri kadar 0 eklenir.
* Mesela, hat numarası 123 ise, karakter dizisinin son 4 karakteri "0123" olacaktır.
*
* Alan kodunun ardından kapatılan parantezden sonra bir boşluk karakteri
* geldiğini gözden kaçırmayın.
*/
@Override
public String toString() {
return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);
}
Eğer bir format belirlemezseniz, aşağıdaki gibi biri dokümantasyon yapabilirsiniz:
/**
* Bu iksirin kısa bir açıklamasını üretir. Üretilen karakter dizisi ileride değişebilir
* aşağıda tipik bir örnek verilmektedir:
*
* "[İksir No: #9: tür=aşk, koku=neft yağı, görünüm=çini mürekkebi]"
*/
@Override
public String toString() { ... }
Bu açıklamayı okuduktan sonra bu formatı baz alarak kod yazan programcılar, format değiştiği zaman kendilerinden başka suçlayacak kimseyi bulamayacaklardır.
Format belirleseniz de belirlemeseniz de, toString
tarafından döndürülen değerde bulunan bütün alanlara erişim sağlayın. Örneğin, PhoneNumber
sınıfı alan kodu, ön ek ve hat numarası alanlarına erişim metotları sağlamalıdır. Eğer bunu yapmazsanız, sınıfınızı kullanan programcıları toString metodunun döndürdüğü karakter dizisi ile çalışmaya zorlamış olursunuz. Bu durum da performans kaybına ve gereksiz kod yazılmasına yol açar, hatalı kod ihtimalini artırır ve format değişikliği olduğunda kolayca bozulan, kırılgan yazılımlara sebebiyet verir. Erişim metotları sağlamayarak, toString
metodunu, ileride değişebileceğini belirtseniz bile sınıfınız için bir API haline getirmiş olursunuz.
Madde 4’de anlatılan statik yardımcı sınıflar için bir toString()
metodu yazmak mantıksızdır. Enum türleri içinse yazmaya gerek yoktur çünkü Java zaten iyi yazılmış bir toString()
sağlamaktadır.
Google’ın sağladığı AutoValue aracı ve kod düzenleyicilerin çoğu otomatik olarak toString()
yaratmak için kullanılabilir. Bu sayede üretilen metotlar sınıfın içeriği hakkında detaylı bilgi verebilse de, sınıfın özel anlamlarını aktaramazlar. Bu sebeple, örneğin PhoneNumber
sınıfı için otomatik üretilen bir toString()
metodu yeterli olmayabilir çünkü telefon numaralarının standart bir yazılış biçimi vardır ve buna uyulması gerekir. Ancak yine de bu Object
sınıfından gelen versiyona göre çok daha faydalıdır.