`Public static void main(String[] args)` đã chết
(mccue.dev)- Giờ đây, chương trình Java đầu tiên không còn phải bắt đầu bằng public static void main(String[] args) nữa, mà có thể được viết bằng cú pháp đơn giản hóa void main()
- Với cú pháp mới, việc nhập xuất có thể được xử lý chỉ bằng các lệnh gọi đơn giản như IO.readln và IO.println, khiến mã trở nên trực quan hơn rất nhiều
- Các cú pháp dài dòng trước đây như new Scanner(System.in) hay System.out.println trở nên không còn cần thiết
- Sự bất tiện kéo dài bấy lâu nay “cuối cùng cũng chấm dứt”; giờ đây cấu trúc cơ bản của Java trở nên gọn nhẹ hơn, giúp giảm rào cản nhập môn và tăng tính thân thiện trong học tập rõ rệt
- Theo truyền thống, Java yêu cầu một khai báo dài
public static void main(String[] args)để bắt đầu chương trình - Tuy nhiên, tính đến ngày 16/09/2025, khai báo phức tạp của hàm
mainvốn được xem là ví dụ đầu tiên kinh điển của Java đã được thay thế bằng một dạng đơn giản mới - Cách cũ:
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); } } - Cách mới:
void main() { var name = IO.readln("What is your name? "); IO.println("Hello, " + name); } - Từ lâu, cú pháp này đã bị chỉ trích là dài dòng một cách không cần thiết với người mới bắt đầu, và là thứ phải học thuộc như một “câu thần chú”
- Việc đưa vào cú pháp ngắn gọn đã giải quyết sự phiền toái và khó hiểu của khai báo cũ, đồng thời cải thiện khả năng đọc mã, giúp giảm mạnh rào cản khi nhập môn Java
- Không còn dùng các ví dụ cơ bản với việc tạo đối tượng và gọi hàm phức tạp như Scanner, System.out.println nữa
> Good Fucking Riddance = “Cuối cùng cũng biến mất nên thật nhẹ cả người. Vĩnh biệt”
5 bình luận
Nghe như đang nói rằng chỉ vì có thêm một cách mới nên cách cũ đã chết.
Có thật là không thể dùng cách cũ nữa và bắt buộc phải dùng cách mới không?
Wow
Có phải học lại Java không..
mainđã chết.mainmuôn năm!Ý kiến trên Hacker News
Tôi nghĩ mình sẽ nhớ cảm giác dần dần hiểu được những đoạn mã xa lạ như thế này theo thời gian. Khi mới học Python rồi chuyển sang Java, tôi thấy rất lạ vì không biết các kiểu như
voidhayString[]có ý nghĩa gì. Sau khi học về kiểu dữ liệu thì tôi hiểu hơn, rồi tiếp theo là khái niệm class và object, và vì saomainlại tồn tại dưới dạng phương thứcstatic. Khi đào sâu hơn nữa và học cả thời điểm class này được gọi, đoạn mã ban đầu trông chỉ như boilerplate bắt đầu dần trở nên có ý nghĩa. Có lẽ những lập trình viên Java nhiều kinh nghiệm còn đọc ra được nhiều tầng ý nghĩa hơn tôi chỉ từ một dòng. Dù vậy, giờ nó biến mất nên cũng thấy nhẹ nhõmThật sự thấy nhẹ nhõm. Trong 30 năm qua, giáo dục phần mềm đã dạy lập trình viên tạo ra sự phức tạp vô ích dưới danh nghĩa “kỹ nghệ”. Ví dụ, lập trình viên A tạo class cho bất cứ thứ gì khi cần, như entry point, callback, interface, v.v. Kết quả là chúng ta có class. Lập trình viên B thêm biến instance vào class, biến toàn bộ hệ thống thành thứ có thể bị thay đổi, từ đó hình thành một “vũng bùn triển khai”. Sau đó, một lập trình viên khác thêm kế thừa để tái sử dụng mã, và điều đó kéo theo nhiều phức tạp hơn cùng cơn ác mộng dynamic dispatch. Cuối cùng xuất hiện cả vòng tham chiếu (
reference cycle), làm quan hệ liên kết giữa các object rối tung lên. Theo thời gian, refactoring ngày càng khó và phiền, đến mức cuối cùng người ta xóa mã đi và làm lại từ đầuTôi nhớ lần đầu học lập trình ở đại học, nhìn thấy đoạn mã này và giáo sư nói kiểu “bình thường tôi sẽ giải thích hết đoạn mã, nhưng chỗ này thì cứ tạm chấp nhận trước đã”. Về sau nhìn lại, lúc nhận ra mình đã hiểu hoàn toàn đoạn mã đó là một trải nghiệm khá ngầu. Nhưng có vẻ thời thế giờ đã thay đổi nên cũng thấy khoan khoái
Thực ra phương thức class
staticgần như là một kiểu phủ nhận thực tế rằng single entry point của chương trình không nhất thiết phải gắn với class. Không chỉ C++, mà cả các ngôn ngữ thừa nhận thực tế thủ tục như Python hay Ruby cũng vậy; còn Java thì như thể bịt mắt người dùng rồi ép họ vào một “thế giới OOP hoàn hảo”Ở góc độ người đã quen với cách làm cũ dựa trên class, phong cách mới hơn (như unnamed classes của Java 21) lại làm tôi có thêm câu hỏi. Tôi tự hỏi liệu Java “mọi thứ đều là object” có thực sự đang bị biến thành ngôn ngữ thủ tục hay không. Nếu cần đối số dòng lệnh thì sẽ làm thế nào? Tôi thường tránh Java nên không rõ lắm, nhưng đây là câu hỏi thật lòng
Thời Java 1.2, người ta đọc standard input như sau.
Scannerclass vẫn còn khá lạ với tôiTôi cũng có trải nghiệm tương tự. Vì từng làm Java từ lâu nên mẫu
public static void mainthì quen thuộc, nhưng cách dùngScannerlại thấy gượng gạo nên tôi chưa từng hiểu chính xác vì saoTheo kinh nghiệm của tôi, vì làm trong các dự án lớn suốt hàng chục năm nên cách viết hàm
mainhoàn toàn không ảnh hưởng gì đến công việc hằng ngày hay sự nghiệp của tôi. Tôi tò mò không biết thay đổi này thực tế tác động lớn đến mức nàoNếu là lập trình viên Android thì vốn dĩ đã không có khái niệm
main. Thành thật mà nói, tôi không nghĩ việc triển khai mộtHello Worldtầm thường trông xấu có phải là thước đo cho việc ngôn ngữ đó xử lý tốt những thứ vượt xa mức đó hay khôngRất nhiều người bước vào lập trình với Java và đã gõ
mainhàng chục lần mà hoàn toàn không biết nó có ý nghĩa gìVới lập trình viên đi làm thì tác động không lớn, nhưng với người mới bắt đầu thì đây là vấn đề quan trọng. Trước đây tôi từng dạy Java, và “câu thần chú” phải học thuộc (boilerplate) trước cả khi in được
Hello, Worldđã trở thành rào cản gia nhập đối với sinh viênNếu làm giao diện dòng lệnh thì có thể sẽ có tác động, nhưng trường hợp tạo CLI bằng Java thì cũng không nhiều
Khoảng 5 năm gần đây tôi chưa thấy
maintrong codebase nào. Và thay vì tự tạo class mới, thông thường tôi chỉ triển khai hoặc mở rộng các class đặc thù của frameworkJEP 445: Unnamed Classes and Instance Main Methods đã được phát hành trong Java 21
https://openjdk.org/jeps/445
Thật ngạc nhiên khi sau 30 năm Java cuối cùng cũng tiến hóa thành một ngôn ngữ khá ổn
Tôi từng viết rất nhiều mã chỉ với Java và C++ thuần, không framework, và khi đó boilerplate ít hơn nhiều, đơn giản hơn nhiều. Thứ khiến tôi thật sự cảm nhận được sự tiện lợi là khoảng 10 năm trước khi lambda được thêm vào. Từ sau khi có lambda, lượng mã giảm đi và trải nghiệm trở nên dễ chịu hẳn. Java trong thời gian dài quá mức tường minh, và có quá nhiều lặp lại không cần thiết, như thể chỉ để kiểm tra lỗi gõ ở cả những chuyện nhỏ nhặt. Không khí đó kéo dài cho đến trước khi Kotlin nổi lên; sau đó với lambda và anonymous class, trải nghiệm phát triển đã cải thiện đáng kể
Nói Java là một ngôn ngữ khủng khiếp thì hơi cường điệu quá
Trong khi đó, tôi lại thấy những ngôn ngữ như Python sau 30 năm vẫn còn bất tiện
Có lẽ là bạn chưa từng trải qua những ngôn ngữ thực sự tệ, hoặc tiêu chuẩn cho “không khủng khiếp” của bạn cao bất thường
Dù vậy, Java có khả năng tương thích ngược và hệ thống quản lý package rất xuất sắc, điểm đó thực sự đáng nể
Đây là điều tôi từng nhắc ở bình luận trước: với tư cách là một lập trình viên Java, Python với tôi ngược lại mới là thứ kỳ quặc, nhưng tôi không nghĩ cách hiện tại là tệ. Tuy nhiên, nếu học lập trình ngay mà chưa hiểu những nền tảng cơ bản của abstraction thì có thể đó không phải lựa chọn tốt cho “nhập môn lập trình”. Khi một công cụ được thiết kế theo hướng mục đích hay paradigm nhất định, đôi khi abstraction có thể bị đẩy đến cực đoan. Java được thiết kế tập trung vào OOP nên kiểu tư duy theo các khối như interface là điều tự nhiên. Access modifier của method/class cũng được phân biệt rõ tùy đối tượng phục vụ là ai (
public,protected, default,private, v.v.). Nói cách khác, việc nhập môn Java bắt đầu từ chuyện phơi bày interface hướng đến người dùng (lập trình viên). Có thể trông kỳ lạ, nhưng ít nhất nó nhất quánCú pháp nặng nề không liên quan trực tiếp đến OOP. Trước khi Java ra đời, không có định nghĩa OOP nào nói rằng “hàm không thể tồn tại bên ngoài class”. Sun đã từ chối khái niệm standalone function trong một thời gian dài (
static importlà Java 5, closure là Java 8, compact source file/instance main thì đến giờ mới có), và vì thế mọi hàm vẫn nằm bên trong class (vì tính thực dụng và khả năng tương thích, chứ không phải vì triết lý). Họ cố chấp với ý tưởng “mọi thứ đều là object”, nhưng trên thực tế lại mâu thuẫn khi đưa vào các giá trị primitive. Không có lợi ích thực tiễn nào bắt buộc hàm phải nằm trong class. Thậm chí sẽ tốt hơn nếu có thể định nghĩa method cho các giá trị như số nguyên. Nhân tiện, trong những ngôn ngữ “OOP thuần” như Smalltalk, bạn có thể tự do định nghĩa hàm ngoài class và chạy mã trực tiếp trong REPLLiên kết liên quan
Hello Worldquan trọng vì nó cho thấy đồng thời cả boilerplate cần để chạy chương trình lẫn phần mã thực sự dùng để in ra kết quả. Ngoài ra, xét ở góc độ cấu hình công cụ build, một phần trong đó không hẳn là “boilerplate” mà là quy trình để thực hành/cấu hình công cụ. Nếu giải thích ngay từ đầu thì đó cũng không phải vấn đề quá lớnNếu chỉ dùng mẹo compiler để tạo class bên trong thì đây chỉ là thay đổi mang tính trình diễn. Nếu các top-level function khác vẫn không được phép thì rốt cuộc nó chỉ là một trường hợp đặc biệt nên vẫn thấy gượng gạo
C# hỗ trợ top level statement từ 9.0, nhưng cuối cùng bên trong nó vẫn được chuyển thành phương thức tĩnh. Nhiều top level function cũng hoạt động tương tự. Ví dụ thực tế: C# decompiled example
Những mẹo như vậy thực ra là ví dụ cho các phép biến đổi compiler hữu ích (closure, async state machine, v.v.). Thay đổi lần này cũng thuộc kiểu đó
Trong C/C++, việc chỉ
main()mới mặc định áp dụngreturn 0cũng tạo cảm giác gượng gạo tương tựNếu chỉ cho phép
mainlà top-level function còn những thứ khác thì không, thì tôi cũng không thấy đây là thay đổi đặc biệt hayTôi không hiểu vì sao trong các ví dụ mã Java người ta lại lược bỏ dòng
import. Nếu muốn cho thấy sự phức tạp của boilerplate thì có lẽimportcũng phải được viết ra đầy đủThường thì IDE tự quản lý
importnên người ta không bận tâm. Với các class liên quan đến IO thì trong ví dụ compact thậm chí không cầnimportriêng, nên mã thực sự chạy được đúng như vậyHầu hết IDE cho Java đều tự động quản lý
importNếu trừu tượng hóa entry point
public static void main(String[] args)theo mô hình top-level statement như C#, có vẻ sẽ giảm boilerplate và làm mã gọn hơn; tôi tò mò không hiểu vì sao họ không làm như vậyHãy xem tài liệu Paving the on-ramp
Nếu entry point tồn tại như một trường hợp ngoại lệ đặc biệt thì tức là đã thừa nhận giới hạn của OOP rồi. Vậy thì chẳng phải nên mạnh dạn thiết kế một phương án thay thế mới hay sao?
Tôi thấy cách unnamed class khá hay ở chỗ nó giúp triển khai biến toàn cục dễ hơn, hỗ trợ hot reload, đồng thời cú pháp ngắn gọn hơn. Mặt khác, file Java bị ràng buộc phải trùng tên với
public class, nên đáng lẽ chỉ cần biến phầnpublic class X { }thành tùy chọn và chỉ viết khi cần là được. Tôi không thật sự hiểu vì sao lại phải đưa vào unnamed classCompiler vẫn chuyển nó thành một class như
HelloWorld.class, nên cái tên đó chỉ là chi tiết triển khai và trên thực tế không thể dùng trực tiếp trong source codehttps://openjdk.org/jeps/445