Java String.formatted: ให้ template อยู่หน้าค่าที่นำมาเติม
ข้อความอย่าง String.format("order=%s total=%.2f", id, total) ทำงานดี แต่เวลา template เป็นค่าหลักที่เราอยากอ่านก่อน Java มี String.formatted ให้เขียน "order=%s total=%.2f".formatted(id, total) ความสามารถ formatting เหมือน String.format(this, args) แต่ลำดับอ่านจาก template ไปค่าที่เติมครับ
WHY: ลดระยะห่างระหว่างข้อความกับค่าที่ใส่
ให้นึกถึงแม่พิมพ์ที่มีช่องว่างอยู่แล้ว เราวางแม่พิมพ์ก่อนแล้วป้อนชิ้นค่าเข้าไปตามลำดับ เมื่อ chain ต่อจาก text block หรือ constant ที่ชื่อดี โค้ดอ่านง่ายกว่าการเปิด String.format( แล้วค่อยตามหา template ในวงเล็บ
นี่เป็นเรื่อง readability ไม่ใช่ performance shortcut และไม่ใช่ template engine HTML มันยังใช้กติกา Formatter มี conversion, width, precision และ exception แบบเดียวกัน
HOW: format ค่าและทดสอบ type/precision
public class Main {
record Order(String id, int itemCount, double total) {}
static String summary(Order order) {
return "order=%s items=%d total=%.2f"
.formatted(order.id(), order.itemCount(), order.total());
}
public static void main(String[] args) {
String result = summary(new Order("A-17", 3, 125.5));
assert result.equals("order=A-17 items=3 total=125.50") : result;
boolean rejectedWrongType = false;
try {
"items=%d".formatted("three");
} catch (java.util.IllegalFormatException expected) {
rejectedWrongType = true;
}
assert rejectedWrongType;
System.out.println("String.formatted checks passed");
}
}
%s, %d, %.2f เป็นสัญญาระหว่าง template กับชนิดค่า Compiler ไม่ตรวจ mismatch เหล่านี้ล่วงหน้า จึงต้องมี test ของข้อความที่เป็น contract เช่น export line, protocol หรือข้อความที่ parser อื่นอ่าน หากเป็น UI copy ควรทดสอบโครงสร้างและ localization โดยไม่ผูกทุกช่องว่างจนแก้ถ้อยคำยาก
Locale เป็นส่วนหนึ่งของผลลัพธ์
formatted ใช้ default locale เช่นเดียวกับ String.format(format, args) ตามเอกสาร ถ้าข้อความเป็น machine-readable อย่าพึ่ง locale ของเครื่อง deploy เพราะ decimal separator หรือ grouping อาจเปลี่ยน ใช้ String.format(Locale.ROOT, ...) เมื่อจำเป็นต้องตรึง locale; formatted ไม่มี overload รับ Locale จึงไม่ใช่คำตอบทุกกรณี
สำหรับข้อความผู้ใช้จริงควรใช้ message catalog/ICU-style localization ที่รองรับการสลับลำดับคำ พหูพจน์ และไวยากรณ์ ไม่ฝังประโยคอังกฤษแล้วแทนค่าด้วย %s ไปทุกภาษา ส่วนตัวเลขเงินต้องใช้ชนิด decimal และ currency formatter ตามกติกาธุรกิจ ไม่ใช้ double จากตัวอย่างไปทำบัญชี
อย่าให้ user คุม format string
ถ้าผู้ใช้ส่ง %d, width มหาศาล หรือ conversion ที่ไม่ตรงชนิดเข้ามา เขาสามารถทำให้เกิด exception หรือใช้ทรัพยากรเกินคาด Template ต้องเป็นค่าที่ทีมควบคุม ส่วนข้อมูลผู้ใช้เป็น argument เท่านั้น และต้อง escape ตาม output context หากจะนำผลไป HTML, SQL, shell หรือ log formatted ไม่ได้ทำ escaping ให้
สำหรับ structured logging อย่าสร้างข้อความก่อนถ้า logger รองรับ field เพราะจะเสีย type, ทำ query ยาก และอาจ format ทั้งที่ log level ปิดอยู่ ใช้ formatted กับข้อความปลายทางที่เป็น string จริง ไม่ใช่แทน logging API หรือ SQL parameterization
Recap
- ใช้
formattedเมื่อ template ควรนำหน้าและ locale default ยอมรับได้ - ใช้
String.format(Locale.ROOT, ...)กับ machine-readable output - template เป็น trusted constant; user input เป็น argument
- ทดสอบ conversion type, precision และข้อความที่เป็น contract
- อย่าใช้แทน HTML escaping, SQL parameters หรือ structured logging
ความสะดวกของเมธอดนี้อยู่ที่การอ่านโค้ดครับ ถ้าต้องแลกกับ locale ที่ไม่ชัดหรือเอา user template เข้ามา ความสั้นหนึ่งบรรทัดไม่คุ้มกับ contract ที่เปราะ