Public static void main(String[] args) ตายแล้ว
(mccue.dev)- ตอนนี้โปรแกรมแรกของ Java ไม่จำเป็นต้องเริ่มด้วย public static void main(String[] args) อีกต่อไป และสามารถเขียนด้วยไวยากรณ์แบบย่อ void main() ได้
- ในไวยากรณ์ใหม่นี้ สามารถจัดการอินพุต/เอาต์พุตได้ด้วยการเรียกง่าย ๆ อย่าง IO.readln และ IO.println ทำให้โค้ดตรงไปตรงมาขึ้นมาก
- ไวยากรณ์ที่ยืดยาวอย่าง new Scanner(System.in), System.out.println จึงไม่จำเป็นอีกต่อไป
- ความไม่สะดวกที่มีมาตลอด “จบสิ้นลงเสียที” และตอนนี้เมื่อโครงสร้างพื้นฐานของ Java เบาขึ้น กำแพงการเริ่มต้นเรียนรู้ก็จะต่ำลงและเป็นมิตรกับผู้เรียนมากขึ้น
- ตามธรรมเนียมแล้ว Java ต้องการการประกาศยาว ๆ ว่า
public static void main(String[] args)เพื่อเริ่มต้นโปรแกรม - แต่ ณ วันที่ 16 กันยายน 2025 การประกาศฟังก์ชัน
mainที่ซับซ้อนซึ่งเคยถูกมองว่าเป็นตัวอย่างแรกสุดของ Java ได้ถูกแทนที่ด้วยรูปแบบใหม่ที่เรียบง่ายกว่า - วิธีเดิม:
public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("What is your name? "); String name = scanner.nextLine(); System.out.println("Hello, " + name); } } - วิธีใหม่:
void main() { var name = IO.readln("What is your name? "); IO.println("Hello, " + name); } - ที่ผ่านมามันถูกวิจารณ์ว่าเป็นไวยากรณ์ที่ยืดยาวเกินจำเป็นสำหรับผู้เริ่มต้น และต้องท่องจำราวกับเป็น “คาถาวิเศษ”
- การนำไวยากรณ์แบบกระชับมาใช้ช่วยแก้ทั้ง ความยุ่งยาก และ ความเข้าใจยาก ของการประกาศแบบเดิม ทำให้โค้ดอ่านง่ายขึ้น และลดกำแพงการเริ่มต้นเรียน Java ลงอย่างมาก
- จะไม่มีการใช้ตัวอย่างพื้นฐานที่ต้องสร้างอ็อบเจ็กต์และเรียกใช้งานที่ซับซ้อนอย่าง Scanner, System.out.println อีกต่อไป
Good Fucking Riddance = “ในที่สุดก็หายไปเสียที โล่งใจจริง ๆ ลาก่อน”
5 ความคิดเห็น
ฟังดูเหมือนว่าพอมีวิธีใหม่เพิ่มเข้ามาอีกวิธี ก็เลยบอกว่าวิธีเดิมตายไปแล้ว
จริง ๆ แล้วหมายความว่าไม่สามารถใช้วิธีเดิมได้อีกต่อไป และจำเป็นต้องใช้วิธีใหม่เท่านั้นจริงหรือ?
ว้าว
ต้องกลับไปเรียนรู้ Java ใหม่อีกครั้งหรือเปล่า..
เมนตายแล้ว เมนจงเจริญ!
ความเห็นจาก Hacker News
คิดว่าคงจะคิดถึงกระบวนการที่พอโบนัสเวลาแล้วค่อย ๆ เข้าใจโค้ดแปลก ๆ แบบนี้มากขึ้น ตอนที่เรียน Python ก่อนแล้วค่อยย้ายมา Java ตอนแรกไม่รู้เลยว่า type อย่าง
voidหรือString[]หมายถึงอะไร เลยรู้สึกว่ามันน่าสนใจ พอได้เรียนเรื่อง type ก็เริ่มเข้าใจ จากนั้นก็ได้เรียนแนวคิดเรื่อง class กับ object แล้วก็เข้าใจว่าทำไมmainถึงต้องอยู่ในรูป static method พอลงลึกไปอีกก็ได้เรียนว่าคลาสนี้ถูกเรียกเมื่อไร ทำให้โค้ดที่ตอนแรกดูเหมือน boilerplate เฉย ๆ เริ่มมีเหตุผลขึ้นมา คิดว่านักพัฒนา Java ที่มีประสบการณ์มากกว่าผมน่าจะอ่านความหมายจากบรรทัดเดียวนี้ได้มากกว่าผมอีกเยอะ แต่ตอนนี้มันหายไปแล้วก็โล่งใจดีโล่งใจจริง ๆ ตลอด 30 ปีที่ผ่านมา การศึกษาด้านซอฟต์แวร์สอนให้นักพัฒนาสร้างความซับซ้อนที่ไม่จำเป็นภายใต้ชื่อของ "วิศวกรรม" ตัวอย่างเช่น นักพัฒนา A สร้างคลาสตามความจำเป็น ไม่ว่าจะเป็น entry point, callback, interface หรืออะไรก็ตาม ผลก็คือเราได้คลาสมา จากนั้นนักพัฒนา B ก็เพิ่ม instance variable เข้าไป ทำให้ทุกอย่างกลายเป็น mutable และเกิด "บ่อโคลนแห่ง implementation" ขึ้นมา ต่อมานักพัฒนาอีกคนเพิ่ม inheritance เพื่อ reuse โค้ด แต่สิ่งนี้ก็สร้างความซับซ้อนเพิ่มและฝันร้ายเรื่อง dynamic dispatch สุดท้ายก็เกิด reference cycle ทำให้ความเชื่อมโยงของ object พันกันยุ่งไปหมด ยิ่งเวลาผ่านไป refactor ก็ยิ่งยากและน่ารำคาญ จนสุดท้ายต้องลบโค้ดทิ้งแล้วเริ่มใหม่
ตอนเรียนเขียนโปรแกรมครั้งแรกในมหาวิทยาลัย ผมจำได้ว่าเห็นโค้ดนี้แล้วอาจารย์บอกว่า "ปกติผมจะอธิบายโค้ดทุกส่วนให้ฟัง แต่ส่วนนี้ให้รับไปก่อนก็แล้วกัน" ตอนหลังพอกลับมามองอีกทีแล้วรู้ตัวว่าผมเข้าใจโค้ดนั้นทั้งหมด มันเป็นประสบการณ์ที่เจ๋งทีเดียว ถึงอย่างนั้นตอนนี้ก็ดูเหมือนยุคสมัยกำลังเปลี่ยนไป เลยรู้สึกโล่งมาก
จริง ๆ แล้ว static class method ก็แทบจะเป็นการปฏิเสธความจริงที่ว่า entry point เดียวของโปรแกรมไม่จำเป็นต้องผูกกับ class ก็ได้ มีทั้ง C++ และภาษาอย่าง Python, Ruby ที่ยอมรับความจริงแบบ procedural นี้ แต่ Java เหมือนปิดตาผู้ใช้แล้วบังคับเข้าไปอยู่ใน "โลก OOP ที่สมบูรณ์แบบ"
ในฐานะคนที่รู้จักแนวทาง class-based แบบเก่าอยู่แล้ว สไตล์ใหม่ ๆ (เช่น unnamed classes ใน Java 21) กลับทำให้เกิดคำถามมากขึ้น สรุปแล้ว Java ที่เคยบอกว่า "ทุกอย่างคือ object" ถูกเปลี่ยนให้เป็นภาษาเชิงกระบวนการจริงหรือเปล่า แล้วถ้าต้องใช้ command-line arguments จะทำอย่างไร ผมแทบจะเลี่ยง Java มาตลอดเลยไม่ค่อยรู้ แต่ถามด้วยความสงสัยจริง ๆ
สมัย Java 1.2 เราอ่าน standard input กันแบบนี้
Scannerclass นี่สำหรับผมยังใหม่และไม่คุ้นผมก็มีประสบการณ์คล้ายกัน เมื่อก่อนเคยเขียน Java มานานจนคุ้นกับแพตเทิร์น
public static void mainแต่พอมาเจอวิธีใช้Scannerกลับรู้สึกแปลก ๆ เลยไม่เคยเข้าใจเหตุผลได้อย่างชัดเจนจากประสบการณ์ของผมที่ทำงานกับโปรเจ็กต์ขนาดใหญ่มาหลายสิบปี วิธีที่เขียน main function แทบไม่มีผลอะไรกับชีวิตประจำวันหรืออาชีพของผมเลย อยากรู้ว่าทุกคนคิดว่าการเปลี่ยนแปลงแบบนี้จะส่งผลจริงมากแค่ไหน
ถ้าเป็นนักพัฒนา Android ก็แทบไม่มีแนวคิดเรื่อง
mainอยู่แล้ว พูดตรง ๆ ผมไม่คิดว่าการที่ Hello World แบบ trivial ดูน่าเกลียด จะเป็นตัวชี้วัดได้ว่าภาษานั้นจัดการเรื่องที่ยากกว่านั้นได้ดีแค่ไหนคนจำนวนมากเริ่มต้นเขียนโปรแกรมด้วย Java และพิมพ์
mainซ้ำเป็นสิบ ๆ ครั้ง โดยไม่รู้เลยว่ามันหมายถึงอะไรสำหรับนักพัฒนาในสายงานจริงอาจไม่ได้มีผลมาก แต่สำหรับมือใหม่มันสำคัญ ผมเคยสอน Java มาก่อน และคาถาวิเศษที่ต้องท่องจำ (boilerplate) ก่อนจะพิมพ์ "Hello, World" ได้ เป็นกำแพงในการเริ่มต้นสำหรับนักเรียนจริง ๆ
ถ้าคุณสร้าง command-line interface ก็อาจได้รับผลกระทบบ้าง แต่คนที่ทำ CLI ด้วย Java ก็ไม่ได้มีเยอะนัก
ช่วงประมาณ 5 ปีหลังมานี้ ผมไม่เคยเห็น
mainใน codebase เลย และแทนที่จะสร้างคลาสใหม่เอง ก็มักจะเป็นการ implement หรือ extend คลาสเฉพาะของ framework มากกว่าJEP 445: Unnamed Classes and Instance Main Methods ถูกปล่อยออกมาใน Java 21
https://openjdk.org/jeps/445
น่าทึ่งที่หลังจาก 30 ปี ในที่สุด Java ก็วิวัฒนาการมาเป็นภาษาที่ค่อนข้างดีได้จริง ๆ
ผมเคยเขียนโค้ดจำนวนมากด้วย Java และ C++ แบบล้วน ๆ ไม่พึ่ง framework ซึ่งตอนนั้น boilerplate น้อยกว่าและง่ายกว่ามาก จุดที่ผมรู้สึกถึงความสะดวกจริง ๆ คือราว 10 ปีก่อนตอนมี lambda เข้ามา หลังจากมี lambda แล้ว ปริมาณโค้ดลดลงและรู้สึกเขียนได้สบายขึ้นมาก Java เคยเป็นภาษาที่ explicit มากเกินไปอยู่นาน และมีการเขียนซ้ำที่ไม่จำเป็นจำนวนมาก เหมือนช่วยได้แค่ตรวจ typo ในเรื่องเล็ก ๆ บรรยากาศแบบนี้อยู่ต่อเนื่องจนก่อน Kotlin จะมา หลังจากนั้นการมี lambda กับ anonymous class ก็ทำให้ประสบการณ์พัฒนาดีขึ้นมาก
คำกล่าวที่ว่า Java เป็นภาษาที่แย่มากนั้นเกินจริงไปเยอะ
ในทางกลับกัน ผมกลับคิดว่าภาษาอย่าง Python ต่อให้ผ่านไป 30 ปีก็ยังใช้งานไม่สะดวกอยู่ดี
เดาว่าคุณอาจไม่เคยเจอภาษาที่แย่จริง ๆ หรือไม่อย่างนั้นเกณฑ์ของคำว่า "ไม่แย่" ก็คงสูงมาก
ถึงอย่างนั้น Java ก็ยอดเยี่ยมมากในเรื่อง backward compatibility และระบบจัดการ package
นี่คือสิ่งที่ผมเคยพูดไว้ในคอมเมนต์ก่อนหน้านี้ ในฐานะนักพัฒนา Java ผมกลับมองว่า python แปลกกว่า แต่ก็ไม่ได้คิดว่าวิธีปัจจุบันมันแย่ เพียงแต่ถ้ายังไม่เข้าใจพื้นฐานของ abstraction แล้วกระโดดเข้าเขียนโปรแกรมเลย มันอาจไม่ใช่ตัวเลือกที่ดีนักสำหรับ "การเริ่มต้นเขียนโปรแกรม" เมื่อเครื่องมือถูกออกแบบโดยยึดตามเป้าหมายหรือ paradigm ใด paradigm หนึ่ง บางครั้งก็อาจเกิด abstraction แบบสุดโต่งได้ Java ถูกออกแบบโดยเน้น OOP จึงทำให้การคิดเป็นบล็อกอย่าง interface ดูเป็นธรรมชาติ access modifiers ของ method/class ก็แยกชัดเจนตามว่าออกแบบไว้ให้ใครใช้ (
public,protected,default,privateเป็นต้น) กล่าวคือ การเริ่มต้นกับ Java เริ่มจากการเปิดเผย interface ไปยังผู้ใช้ (programmer) มันอาจดูแปลก แต่ก็อย่างน้อยก็มีความสม่ำเสมอไวยากรณ์ที่หนักไม่ได้เกี่ยวข้องกับ OOP โดยตรง ก่อนที่ Java จะเกิดขึ้น ไม่มีนิยาม OOP แบบไหนที่บอกว่า "ฟังก์ชันต้องอยู่นอกคลาสไม่ได้" Sun ปฏิเสธแนวคิดเรื่อง standalone function มานานมาก (
static importมาใน Java 5, closure มาใน Java 8, compact source file/instance main เพิ่งมาเอาตอนนี้) ผลก็คือทุกฟังก์ชันยังคงอยู่ภายในคลาสทั้งหมด (เพราะเหตุผลด้านการใช้งานจริงและความเข้ากันได้ ไม่ใช่เหตุผลเชิงปรัชญา) เขาพยายามยืนกรานแนวคิด "ทุกอย่างคือ object" แต่ในทางปฏิบัติก็กลับขัดแย้งด้วยการมี primitive values เข้ามา ไม่มีประโยชน์เชิงปฏิบัติอะไรเลยที่ฟังก์ชันต้องอยู่ในคลาส ตรงกันข้าม ถ้าสามารถนิยาม method ให้กับค่าอย่าง integer ได้ก็น่าจะดีกว่าด้วยซ้ำ อ้างอิงไว้ว่า ภาษา "pure OOP" อย่าง Smalltalk ก็สามารถนิยามฟังก์ชันนอกคลาสได้อย่างอิสระ และยังรันโค้ดได้ตรง ๆ ใน REPL ด้วยลิงก์ที่เกี่ยวข้อง
ที่ Hello World สำคัญก็เพราะมันแสดงทั้ง boilerplate ที่ต้องมีเพื่อรันโปรแกรม และโค้ดสำหรับแสดงผลจริงได้พร้อมกัน อีกมุมหนึ่ง ในเรื่องการตั้งค่า build tools บางส่วนก็ไม่ใช่แค่ 'boilerplate' แต่เป็นขั้นตอนสำหรับฝึกใช้เครื่องมือ/ตั้งค่าเครื่องมือด้วย ถ้าอธิบายให้ตรงนั้นตั้งแต่แรกก็ไม่ได้เป็นปัญหาใหญ่อะไร
ถ้าแค่ใช้กลเม็ดของ compiler เพื่อสร้างคลาสภายใน ก็เป็นเพียงการเปลี่ยนเพื่อภาพลักษณ์เท่านั้น ถ้ายังไม่ยอมให้มี top-level function อื่น ๆ ได้ สุดท้ายมันก็ยังเป็นแค่กรณีพิเศษที่ดูแปลกอยู่ดี
C# รองรับ top level statement ตั้งแต่ 9.0 แต่สุดท้ายภายในก็ถูกแปลงเป็น static method อยู่ดี top level function หลายตัวก็ทำงานคล้ายกัน ตัวอย่างจริง: C# decompiled example
กลเม็ดแบบนี้เป็นตัวอย่างของการแปลงโดย compiler ที่มีประโยชน์จริง (เช่น closure, async state machine เป็นต้น) การเปลี่ยนครั้งนี้ก็เข้าข่ายเดียวกัน
ใน C/C++ ที่มีการใช้ default
return 0เฉพาะในmain()ก็ให้ความรู้สึกแปลกคล้ายกันถ้ายอมให้เฉพาะ
mainเป็น top-level function ได้ แต่ที่เหลือทำไม่ได้ ผมก็ยังมองว่านี่ไม่ใช่การเปลี่ยนแปลงที่ดีนักผมไม่เข้าใจว่าทำไมตัวอย่างโค้ด Java ถึงชอบละบรรทัด
importกัน ถ้าจะโชว์ความซับซ้อนของ boilerplate ผมว่าต้องใส่importให้ครบด้วยปกติ IDE จะจัดการ
importให้อัตโนมัติอยู่แล้ว เลยไม่ค่อยมีใครใส่ใจ และถ้าเป็นคลาสเกี่ยวกับ IO ในตัวอย่างแบบ compact ก็ไม่ต้องมีimportเพิ่ม โค้ดเลยรันได้ตามนั้นจริงJava IDE ส่วนใหญ่จัดการ
importให้อัตโนมัติถ้าจะลด boilerplate และทำให้โค้ดกระชับขึ้น โดย abstract entry point
public static void main(String[] args)ให้เป็นแนวคิด top-level statement แบบ C# ก็ดูน่าจะดี เลยสงสัยว่าทำไมถึงไม่ทำแบบนั้นแนะนำให้อ่านเอกสาร Paving the on-ramp
ถ้า entry point มีอยู่ในฐานะกรณียกเว้นพิเศษ ก็เท่ากับยอมรับข้อจำกัดของ OOP ไปแล้ว อย่างนั้นอาจจะดีกว่าถ้าออกแบบทางเลือกใหม่แบบจริงจังไปเลย
ผมคิดว่าการใช้ unnamed class ช่วยให้ทำ global variable ได้ง่ายขึ้น รองรับ hot reload ได้ และไวยากรณ์ก็กระชับขึ้น ตรงนี้ถือว่าดี แต่ในเมื่อ Java file ก็มีข้อจำกัดอยู่แล้วว่าชื่อต้องตรงกับ
public classทำไมไม่ทำให้ส่วนpublic class X { }เป็น optional แล้วค่อยเขียนเมื่อจำเป็นแทน ผมยังไม่ค่อยเข้าใจว่าทำไมต้องเพิ่ม unnamed class เข้ามาcompiler ก็ยังแปลงมันเป็นคลาสอย่าง
HelloWorld.classอยู่ดี ดังนั้นชื่อนั้นก็เป็นแค่รายละเอียดระดับ implementation และใน source code จริงก็อ้างถึงตรง ๆ ไม่ได้อยู่แล้วhttps://openjdk.org/jeps/445