Menu

Varargs trong Java: phương thức nhận số lượng đối số tùy ý

Cách varargs (...) trong Java cho phép một phương thức nhận số lượng đối số tùy ý, cách chúng trở thành mảng, quy tắc một varargs đứng cuối, cùng các bẫy khi gọi rỗng và khi mơ hồ.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Một phương thức, số lượng đối số tùy ý

Đôi khi bạn không biết trước một phương thức cần bao nhiêu đối số. Lúc này bạn muốn max(3, 9), lúc khác lại là max(1, 7, 4, 2, 8). Nếu không có varargs, bạn sẽ phải viết một phiên bản nạp chồng cho mỗi số lượng hoặc bắt nơi gọi tự tạo mảng. Varargs, viết tắt của variable arguments (đối số biến đổi), cho phép một phương thức duy nhất nhận không, một hoặc nhiều giá trị cùng kiểu.

Bạn khai báo bằng cách đặt ba dấu chấm (...) sau kiểu của tham số:

Nơi gọi truyền các giá trị rời; bên trong phương thức, numbers là một mảng thật. Ý tưởng chỉ có vậy: trình biên dịch gom các đối số vào một mảng giúp bạn.

Varargs chỉ là một mảng

Lúc chạy không có phép màu nào cả. Một tham số khai báo int... numbers chính xác là một int[] khi bạn ở bên trong phương thức. Bạn có thể lấy length, truy cập theo chỉ số và lặp qua nó như mọi mảng khác.

Vì nó thật sự là một mảng, bạn cũng có thể đưa cho phương thức varargs một mảng có sẵn: nó chấp nhận cả hai cách:

Quy tắc: đứng cuối và chỉ một

Hai quy tắc giúp varargs không bị mơ hồ, và trình biên dịch bắt buộc cả hai:

  1. Tham số varargs phải đứng cuối. Tham số thông thường đứng trước, rồi tham số ... nhận hết phần còn lại.
  2. Một phương thức có tối đa một tham số varargs. Có hai thì không thể biết cái này kết thúc ở đâu và cái kia bắt đầu từ đâu.

Đoạn này biên dịch được. Nhưng đặt varargs lên đầu, static void log(Object... values, String level), là lỗi biên dịch: varargs parameter must be the last. Tham số bắt buộc level luôn đứng trước ....

Bạn đã dùng varargs ở đâu

Bạn vẫn luôn gọi các phương thức varargs từ trước đến giờ. System.out.printf và String.format là ví dụ kinh điển: tham số thứ hai của chúng là Object... args:

List.of(...), Arrays.asList(...) và nhiều API kiểu builder cũng dùng varargs. Đó là lý do bạn có thể truyền bao nhiêu phần tử tùy thích cho chúng mà không cần tự bọc vào mảng.

Varargs và nạp chồng

Varargs và nạp chồng (overloading) tương tác theo một cách gây bất ngờ. Khi cả một phiên bản nạp chồng thông thường và một phiên bản varargs đều khớp với lời gọi, Java ưu tiên phiên bản cụ thể hơn: phương thức có số tham số cố định thắng:

pick(1, 2) khớp cả hai, nhưng Java chọn phiên bản không varargs vì nó khớp sát hơn. Phương thức varargs chỉ chạy khi không có phiên bản cố định nào khớp. Thường đây là điều bạn muốn, nhưng nó có nghĩa là thêm một phiên bản varargs có thể lặng lẽ thay đổi phương thức mà một lời gọi trỏ tới.

Cẩn thận với lời gọi rỗng và null

Có hai bẫy hay khiến người ta vấp. Thứ nhất, gọi phương thức varargs mà không có đối số nào sẽ đưa cho phương thức một mảng rỗng, không phải null, nên numbers.length là 0 và vòng lặp không làm gì. Bạn không cần kiểm tra null cho trường hợp không có đối số:

Thứ hai, truyền một null trực tiếp vừa mơ hồ vừa nguy hiểm: Java có thể hiểu count(null) là "cả mảng varargs là null" thay vì "một phần tử null", và khi bạn đọc length của nó sẽ ném ra NullPointerException. Nếu bạn thật sự muốn một phần tử null duy nhất, hãy viết tường minh count((Object) null).

Tiếp theo: lớp

Varargs hoàn thiện những gì bạn có thể làm với tham số của phương thức: danh sách đối số linh hoạt bên cạnh nạp chồng. Cho đến giờ các phương thức chỉ nằm rời rạc trong một lớp với static. Tiếp theo bạn sẽ thấy lớp thật sự hoạt động thế nào: gom dữ liệu (thuộc tính) và hành vi (phương thức) vào các kiểu do chính bạn định nghĩa, đó là trái tim của lập trình hướng đối tượng trong Java.

Câu hỏi thường gặp

Varargs trong Java là gì?

Varargs (variable arguments, đối số biến đổi) cho phép một phương thức nhận số lượng đối số tùy ý cùng kiểu. Bạn khai báo bằng cách đặt ... sau kiểu: int sum(int... numbers). Nơi gọi có thể truyền không, một hoặc nhiều giá trị, và bên trong phương thức numbers là một mảng bình thường. Đây là cú pháp rút gọn: Java gom các đối số rời vào một mảng giúp bạn.

Một phương thức Java có thể có nhiều hơn một tham số varargs không?

Không. Một phương thức chỉ có tối đa một tham số varargs, và nó phải đứng cuối danh sách tham số, ví dụ void log(String level, Object... values). Nếu nó không đứng cuối, Java sẽ không biết phần có độ dài thay đổi kết thúc ở đâu và tham số tiếp theo bắt đầu từ đâu. Tham số thông thường luôn đứng trước tham số ....

Varargs và tham số mảng trong Java khác nhau thế nào?

Lúc chạy chúng gần như giống nhau: tham số varargs int... chính là một int[] bên trong phương thức. Khác biệt nằm ở nơi gọi: với varargs bạn viết sum(1, 2, 3) (các giá trị rời), còn với tham số mảng tường minh bạn phải tạo và truyền một mảng, sum(new int[]{1, 2, 3}). Phương thức varargs cũng nhận trực tiếp một mảng, nên đây là lựa chọn linh hoạt hơn.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu