Tôi sợ sự khác biệt. Tôi không biết sử dụng chúng để làm gì.
Thêm vào đó, cảm thấy nguy hiểm khi để mọi người vượt qua nhiều cuộc tranh luận như họ muốn.
Một ví dụ về bối cảnh sẽ là một nơi tốt để sử dụng chúng là gì?
Tôi sợ sự khác biệt. Tôi không biết sử dụng chúng để làm gì.
Thêm vào đó, cảm thấy nguy hiểm khi để mọi người vượt qua nhiều cuộc tranh luận như họ muốn.
Một ví dụ về bối cảnh sẽ là một nơi tốt để sử dụng chúng là gì?
Câu trả lời:
Varargs rất hữu ích cho bất kỳ phương thức nào cần xử lý số lượng đối tượng không xác định . Một ví dụ điển hình là String.format
. Chuỗi định dạng có thể chấp nhận bất kỳ số lượng tham số nào, vì vậy bạn cần một cơ chế để truyền vào bất kỳ số lượng đối tượng nào.
String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);
Một nguyên tắc tốt sẽ là:
"Sử dụng varargs cho bất kỳ phương thức (hoặc hàm tạo) nào cần một mảng T (bất kể kiểu T có thể) làm đầu vào".
Điều đó sẽ làm cho các cuộc gọi đến các phương thức này dễ dàng hơn (không cần phải làm new T[]{...}
).
Bạn có thể mở rộng quy tắc này để bao gồm các phương thức với một List<T>
đối số, với điều kiện là đối số này chỉ dành cho đầu vào (nghĩa là danh sách không được sửa đổi bởi phương thức).
Ngoài ra, tôi sẽ không sử dụng f(Object... args)
vì nó trượt theo cách lập trình với các API không rõ ràng.
Về các ví dụ, tôi đã sử dụng nó trong DesignGridLayout , nơi tôi có thể thêm một vài JComponent
s trong một cuộc gọi:
layout.row().grid(new JLabel("Label")).add(field1, field2, field3);
Trong đoạn mã trên, phương thức add () được định nghĩa là add(JComponent... components)
.
Cuối cùng, việc thực hiện các phương thức như vậy phải quan tâm đến thực tế rằng nó có thể được gọi với một vararg trống! Nếu bạn muốn áp đặt ít nhất một đối số, thì bạn phải sử dụng một thủ thuật xấu xí như:
void f(T arg1, T... args) {...}
Tôi coi thủ thuật này là xấu vì việc thực hiện phương thức sẽ ít đơn giản hơn so với việc chỉ có T... args
trong danh sách đối số của nó.
Hy vọng điều này sẽ giúp làm rõ quan điểm về varargs.
if (args.length == 0) throw new RuntimeException("foo");
thay thế? (Vì người gọi đã vi phạm hợp đồng)
void f(T arg1, T... args)
điều đó luôn đảm bảo rằng nó không bao giờ được gọi mà không có đối số, không cần phải đợi cho đến khi chạy.
Tôi sử dụng varargs thường xuyên để xuất ra các bản ghi cho mục đích gỡ lỗi.
Khá nhiều lớp trong ứng dụng của tôi có phương thức debugPrint ():
private void debugPrint(Object... msg) {
for (Object item : msg) System.out.print(item);
System.out.println();
}
Sau đó, trong các phương thức của lớp, tôi có các cuộc gọi như sau:
debugPrint("for assignment ", hwId, ", student ", studentId, ", question ",
serialNo, ", the grade is ", grade);
Khi tôi hài lòng rằng mã của tôi đang hoạt động, tôi nhận xét mã trong phương thức debugPrint () để các nhật ký sẽ không chứa quá nhiều thông tin không liên quan và không mong muốn, nhưng tôi có thể để các cuộc gọi riêng lẻ đến debugPrint (). Sau đó, nếu tôi tìm thấy một lỗi, tôi chỉ cần bỏ mã debugPrint () và tất cả các lệnh gọi tới debugPrint () của tôi được kích hoạt lại.
Tất nhiên, thay vào đó, tôi có thể dễ dàng tránh các varargs và thực hiện các thao tác sau:
private void debugPrint(String msg) {
System.out.println(msg);
}
debugPrint("for assignment " + hwId + ", student " + studentId + ", question "
+ serialNo + ", the grade is " + grade);
Tuy nhiên, trong trường hợp này, khi tôi nhận xét mã debugPrint (), máy chủ vẫn phải trải qua sự cố nối tất cả các biến trong mỗi lệnh gọi tới debugPrint (), mặc dù không có gì được thực hiện với chuỗi kết quả. Tuy nhiên, nếu tôi sử dụng varargs, máy chủ chỉ phải đặt chúng vào một mảng trước khi nó nhận ra rằng nó không cần chúng. Rất nhiều thời gian được tiết kiệm.
Các biến có thể được sử dụng khi chúng ta không chắc chắn về số lượng đối số được truyền trong một phương thức. Nó tạo ra một mảng các tham số có độ dài không xác định trong nền và một tham số như vậy có thể được coi là một mảng trong thời gian chạy.
Nếu chúng ta có một phương thức bị quá tải để chấp nhận số lượng tham số khác nhau, thì thay vì quá tải các phương thức khác nhau, chúng ta chỉ có thể sử dụng khái niệm varargs.
Ngoài ra, khi loại tham số sẽ thay đổi thì việc sử dụng "Đối tượng ... kiểm tra" sẽ đơn giản hóa mã rất nhiều.
Ví dụ:
public int calculate(int...list) {
int sum = 0;
for (int item : list) {
sum += item;
}
return sum;
}
Ở đây gián tiếp một mảng kiểu int (danh sách) được truyền dưới dạng tham số và được coi là một mảng trong mã.
Để hiểu rõ hơn hãy theo liên kết này (nó giúp tôi hiểu rất rõ về khái niệm này): http://www.javadb.com/USE-varargs-in-java
PS: Ngay cả tôi cũng sợ sử dụng varargs khi tôi không sử dụng nó. Nhưng bây giờ tôi đã quen với nó. Như người ta nói: "Chúng tôi bám vào cái đã biết, sợ cái chưa biết", vì vậy chỉ cần sử dụng nó nhiều nhất có thể và bạn cũng sẽ bắt đầu thích nó :)
Varargs là tính năng được thêm vào trong phiên bản java 1.5.
Tại sao phải sử dụng cái này?
Làm thế nào điều này hoạt động?
Nó tạo ra một mảng với các đối số đã cho & truyền mảng cho phương thức.
Thí dụ :
public class Solution {
public static void main(String[] args) {
add(5,7);
add(5,7,9);
}
public static void add(int... s){
System.out.println(s.length);
int sum=0;
for(int num:s)
sum=sum+num;
System.out.println("sum is "+sum );
}
}
Đầu ra:
2
tổng là 12
3
tổng là 21
Tôi cũng có một nỗi sợ liên quan đến varargs:
Nếu người gọi chuyển trong một mảng rõ ràng cho phương thức (trái ngược với nhiều tham số), bạn sẽ nhận được một tham chiếu được chia sẻ đến mảng đó.
Nếu bạn cần lưu trữ mảng này trong nội bộ, bạn có thể muốn sao chép nó trước để tránh người gọi có thể thay đổi nó sau.
Object[] args = new Object[] { 1, 2, 3} ;
varArgMethod(args); // not varArgMethod(1,2,3);
args[2] = "something else"; // this could have unexpected side-effects
Mặc dù điều này không thực sự khác với việc chuyển vào bất kỳ loại đối tượng nào có trạng thái có thể thay đổi sau đó, vì mảng thường là (trong trường hợp một cuộc gọi có nhiều đối số thay vì một mảng), một đối tượng mới được tạo bởi trình biên dịch bên trong mà bạn có thể an toàn sử dụng, đây chắc chắn là hành vi bất ngờ.
Tôi sử dụng varargs thường xuyên cho các nhà xây dựng có thể lấy một số loại đối tượng bộ lọc. Ví dụ, một phần lớn trong hệ thống của chúng tôi dựa trên Hadoop dựa trên Mapper xử lý việc tuần tự hóa và giải tuần tự hóa các mục cho JSON và áp dụng một số bộ xử lý mà mỗi mục lấy một mục nội dung và sửa đổi và trả về hoặc trả về null từ chối.
Trong tài liệu Java của Var-Args, việc sử dụng var args khá rõ ràng:
http://docs.oracle.com/javase/1.5.0/docs/guide/lingu/varargs.html
về cách sử dụng nó nói:
"Vì vậy, khi nào bạn nên sử dụng varargs? Là một khách hàng, bạn nên tận dụng chúng bất cứ khi nào API cung cấp chúng. Các ứng dụng quan trọng trong API cốt lõi bao gồm phản chiếu, định dạng thông báo và cơ sở printf mới. Là một nhà thiết kế API, bạn nên sử dụng chúng Nói một cách tiết kiệm, chỉ khi lợi ích thực sự hấp dẫn. Nói chung, bạn không nên quá tải một phương thức varargs, hoặc sẽ rất khó để các lập trình viên tìm ra quá tải nào được gọi. "