toString : เขียนยังไง ?คิด : ผมพอรู้แล้วครับว่าทำไมเราควรเขียนเมท็อด toString( ) แต่ก็ยังสงสัยครับว่าจะเขียนอย่างไรดีครับ ถึงจะเหมือนเพื่อนร่วมอาชีพที่ทำๆ กัน ? พ่อ : เรามาดูกันก่อนว่าใน javadoc comment ของเมท็อด toString ในคลาส Object เขาเขียนไว้ว่าอย่างไร Returns a string representation
of the object. In general, the
เขาก็ไม่ได้บังคับอะไรมาก แค่บอกว่าให้คืนสตริงที่บรรยายตัวออปเจกต์
อย่างสั้นๆ ได้ใจความ เพื่อให้ "คน" อ่าน โดยเขาแนะนำว่า subclass
ทั้งหลายของ Object (ซึ่งก็หมายความว่าทุกๆ คลาสนั่นแหละ) ควร override
เมท็อดนี้toString method returns a
string that "textually represents" this object. The result should be a
concise but informative representation that is easy for a person to
read. It is recommended that all subclasses override this method.01: public class Sample {แปลแล้วสั่งทำงานจะได้ผลดังนี้ java.lang.Object@fabe9ลองมาวิเคราะห์กันดู จากตัวอย่างจะเห็นผลที่ได้ แบ่งคร่าวๆ ได้สามแบบ
คิด : พ่ออธิบายมาต้องนาน แล้วต้องเขียนยังไงดี ? พ่อ : ก็นี่ไง กำลังจะอธิบายพอดี กรณีแรกนั้นง่าย เพราะไม่ต้องเขียน ซึ่งถ้าเราตัดสินใจไม่เขียน toString ก็คงต้องเป็นเพราะมันไม่มีประเด็นที่จะแสดงอะไรให้ผู้ใช้ดูเลย เช่นในกรณีของ java.util.Random หรือกรณีของคลาส Sample ที่เรามีแค่ main ไว้ทดสอบเล่น หรือกรณีของพวก utility class ที่มีแต่ static methods เท่านั้น เช่นคลาส Math ซึ่งไม่มีการสร้างออปเจกต์แต่อย่างใด ก็ไม่ต้องไปแสดงอะไรใครเห็นหรอก กรณีที่สอง เป็นกรณีที่ใช้กันทั่วไปเพื่อช่วยในการ debug โปรแกรม ตัวสตริงเริ่มด้วยชื่อคลาสตามด้วยรายละเอียดของข้อมูลภายในออปเจกต์ มาดูตัวอย่างใน java.awt.Point มีรายละเอียดดังนี้ public String toString() {เขียนง่ายๆ คือเรียก getClass( ).getName( ) ได้สตริงชื่อคลาสของออปเจกต์ (getClass เป็นเมท็อดของคลาส Object ซึ่งเป็นบรรพบุรุษ เพื่อคืนว่าออปเจกต์นี้เป็นคลาสอะไร ส่วน getName ก็คืนสตริงแทนชื่อของคลาสที่ได้จาก getClass) นำชื่อคลาสมาต่อกับค่า x และ y ของจุด มาดูอีกตัวอย่าง toString ของ java.lang.Thread มีรายละเอียดดังนี้ public String toString() {เขียนง่ายกว่ากรณีของ Point เสียอีก เขียน "Thread" เป็นชื่อคลาสตรงๆ เลย ไม่ต้องไปใช้ getClass( ).getName( ) ให้ยุ่งยาก คิด : เอ๊ะ ผมเคยไปดู toString ของหลายๆ คลาส เห็นเขาใช้ getClass( ).getName( ) กันเยอะ ทำไมไม่เขียนแบบของ Thread เล่า ใส่สตริงชื่อคลาสไปตรงๆ ก็สิ้นเรื่อง อย่างของ Point ก็น่าจะเขียน public String toString() {ง่ายกว่า พ่อว่าไง ? พ่อ : กำลังจะบอกพอดีเลยว่า ที่เขาใช้ getClass().getName() ก็เพราะเขาเขียนไว้เผื่อให้ลูกหลานใช้ด้วย สมมติว่าเราเขียนคลาสใหม่ชื่อ MyThread ดังนี้ public class MyThread extends Thread {โปรแกรมข้างบนนี้เราสร้าง MyThread extends Thread การ println ออปเจกต์ของ MyThread ก็จะเรียก toString ของ Thread ซึ่งเป็นคลาสพ่อ ตกทอดมาให้ลูก เมื่อแปลและสั่งทำงานจะได้ Thread[Thread-0,5,main] ดูซิว่ามันไม่ถูก เพราะเราสั่ง toString กับออปเจกต์ของ MyThread แต่สิ่งที่แสดงออกมากลับบอกว่าเป็นของ Thread ที่เป็นเช่นนี้ก็เพราะ toString ของ Thread ดันไปเขียนคำว่า "Thread" ฝังใน toString แต่ถ้าเราใช้ getClass().getName() แทน ก็จะได้ผลเป็น MyThread[Thread-0,5,main] โดยคำว่า MyThread นั้นได้มาจาก getClass().getName() ไปคิดเอาตอน runtime อ่านแล้วสื่อความหมายกว่า โดยสรุป : ถ้าอยากแสดงชื่อคลาสใน toString จงใช้ getClass().getName() แทนการเขียนชื่อคลาสตรงๆ ลงไป คิด : แล้วส่วนที่แสดงข้อมูลภายในออปเจกต์ที่ตามหลังชื่อคลาสล่ะครับ ต้องเขียนอย่างไร ? พ่อ : ขอแจงเป็นข้อๆ ดังนี้
/** ว่าไง พอมั่นใจที่จะเขียน toString ได้หรือยัง ? คิด : เดี๋ยวๆ ยังไม่หมด พ่อยังไม่ได้อธิบาย toString แบบที่สาม อย่างเช่นของ File, Date, และ Double ในตัวอย่างตอนต้นเลย พ่อ : เกือบลืม กรณีที่สาม มักเป็นการคืนสตริงที่มีรูปแบบแน่ชัด (ระบุชัดเจนใน API เลยว่าแต่ละตัวแต่ละตำแหน่งคืออะไร) เช่นกรณีของ java.util.Date เขาก็แสดงตามมาตรฐานสากล ในรูปแบบ dow mon dd hh:mm:ss zzz yyyy ซึ่งพร้อมใช้ประกอบเป็นผลลัพธ์ของโปรแกรมได้ทันที จะเขียนแบบนี้ ก็ไม่จำเป็นต้องแสดงชื่อคลาส เพราะต้องการเห็นเนื้อๆ ของออปเจกต์ เพือนำไปใช้เลย ได้ชื่อคลาสมาก็เกะกะ (ไม่เหมือนกรณีที่สองที่ให้โปรแกรมเมอร์ดู เลยต้องรู้ละเอียดหน่อย) สิ่งที่ต้องคิดหนักก็คือรูปแบบของผลลัพธ์ ถ้าเรากำหนดรูปแบบแล้ว อีกทั้งไปเขียนใน javadoc comment ที่ปรากฏเป็น semantic contract ไว้ ก็ต้องปฏิบัติตามสัญญาที่เขียนไว้ ในภายภาคหน้า เกิดอยากเปลี่ยนใจ จะต้องถูกกล่าวหาแน่นอนว่าใจโลเล ไม่ทำตามสัญญา ผู้ใช้ที่เคยใช้คลาสเราโดยพึ่งรูปแบบที่เราสัญญาไว้ ก็ล้มครืนแน่ถ้าเราไปซี้ซั้วเปลี่ยนในอนาคต ลองอ่านของ java.util.Date เขาเขียนไว้ชัดเจนแค่ไหน Converts this
Date
object to a String of the form: dow mon dd hh:mm:ss zzz yyyy where:
dow is the day of the week (Sun, Mon, Tue, Wed, Thu, Fri, Sat). คิด : ครับๆ พอแล้วครับ ผมรู้แล้วครับว่า ต้องจำข้อความ "the content and format of the
returned string may vary between implementations"
เอาไว้เติมใน javadoc comment เป็นการป้องกันตัวเองไม่ให้ถูกว่าในอนาคต
กลัวเสียชื่อเกร็ดกาแฟ (9 ม.ค. 2547) Copyright 2004 Somchai Prasitjutrakul |