Tôi có thể truyền một mảng làm đối số cho một phương thức với các đối số biến trong Java không?


275

Tôi muốn có thể tạo một chức năng như:

class A {
  private String extraVar;
  public String myFormat(String format, Object ... args){
    return String.format(format, extraVar, args);
  }
}

Vấn đề ở đây là argsđược coi là Object[]trong phương pháp này myFormat, và do đó là một cuộc tranh luận duy nhất để String.format, trong khi tôi muốn mỗi đơn Objecttrong argsđể được thông qua như một cuộc tranh luận mới. Vì String.formatcũng là một phương thức với các đối số biến, nên điều này là có thể.

Nếu điều này là không thể, có một phương pháp như thế String.format(String format, Object[] args)nào? Trong trường hợp đó tôi có thể extraVarchuẩn bị argssử dụng một mảng mới và chuyển nó sang phương thức đó.


8
Tôi không thể không tự hỏi tại sao câu hỏi này là một loại câu hỏi "có hợp lệ" không. Bạn không thể thử nó sao? Đừng hỏi quá nhiều, bạn sẽ làm hại chính mình nhiều hơn là tốt.
kamasheto

21
đủ đúng, điều này có thể đã được kiểm tra dễ dàng. tuy nhiên, điều hay ho về một câu hỏi như thế này là nó phơi bày chủ đề và thu hút những câu trả lời thú vị.
akf

2
Tôi thực sự đã thử đoạn mã trên, với ý định truyền mảng args làm đối số cho phương thức. Tuy nhiên, tôi đã không nhận ra rằng tôi nên trả trước ExtraVar để tranh luận trước. Khi bạn biết rằng các đối số biến được coi là một mảng (thậm chí bên ngoài phương thức), điều này tất nhiên là khá logic.
dùng362382

Câu trả lời:


180

Các loại cơ bản của một phương pháp matrixdic function(Object... args) function(Object[] args) . Sun đã thêm các vararg theo cách này để duy trì khả năng tương thích ngược.

Vì vậy bạn chỉ có thể để thêm vào trước extraVarđến argsvà cuộc gọi String.format(format, args).


Truyền một đối số kiểu X[]vào một phương thức x(X... xs)đưa ra cảnh báo sau trong Eclipse:Type X[] of the last argument to method x(X...) doesn't exactly match the vararg parameter type. Cast to X[] to confirm the non-varargs invocation, or pass individual arguments of type X for a varargs invocation.
Luke Hutchison

318

Có, a T...chỉ là một cú pháp cú pháp cho a T[].

Các tham số định dạng JLS 8.4.1

Tham số chính thức cuối cùng trong một danh sách là đặc biệt; nó có thể là một tham số arity thay đổi , được biểu thị bằng một elipsis theo loại.

Nếu tham số chính thức cuối cùng là tham số arity biến đổi của loại T, nó được coi là xác định tham số chính thức của loại T[]. Phương pháp sau đó là một phương pháp arity thay đổi . Nếu không, nó là một phương pháp arity cố định . Các yêu cầu của một phương thức arity biến có thể chứa nhiều biểu thức đối số thực tế hơn các tham số chính thức. Tất cả các biểu thức đối số thực tế không tương ứng với các tham số chính thức trước tham số arity biến sẽ được ước tính và kết quả được lưu trữ trong một mảng sẽ được chuyển đến lời gọi phương thức.

Đây là một ví dụ để minh họa:

public static String ezFormat(Object... args) {
    String format = new String(new char[args.length])
        .replace("\0", "[ %s ]");
    return String.format(format, args);
}
public static void main(String... args) {
    System.out.println(ezFormat("A", "B", "C"));
    // prints "[ A ][ B ][ C ]"
}

Và vâng, mainphương pháp trên là hợp lệ, bởi vì một lần nữa, String...chỉ là String[]. Ngoài ra, vì các mảng là covariant, a String[]là một Object[], vì vậy bạn cũng có thể gọi ezFormat(args)một trong hai cách.

Xem thêm


Varargs gotchas # 1: chuyền null

Làm thế nào varargs được giải quyết là khá phức tạp, và đôi khi nó làm những điều có thể làm bạn ngạc nhiên.

Xem xét ví dụ này:

static void count(Object... objs) {
    System.out.println(objs.length);
}

count(null, null, null); // prints "3"
count(null, null); // prints "2"
count(null); // throws java.lang.NullPointerException!!!

Do cách giải quyết các varargs, câu lệnh cuối cùng được gọi với objs = null, tất nhiên sẽ gây ra NullPointerExceptionvới objs.length. Nếu bạn muốn đưa ra một nullđối số cho tham số varargs, bạn có thể thực hiện một trong các cách sau:

count(new Object[] { null }); // prints "1"
count((Object) null); // prints "1"

Câu hỏi liên quan

Sau đây là mẫu của một số câu hỏi mà mọi người đã hỏi khi xử lý varargs:


Vararg gotchas # 2: thêm đối số bổ sung

Như bạn đã tìm ra, những điều sau đây không "hoạt động":

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(myArgs, "Z"));
    // prints "[ [Ljava.lang.String;@13c5982 ][ Z ]"

Bởi vì cách thức hoạt động của varargs, ezFormatthực sự có 2 đối số, lần đầu tiên là a String[], lần thứ hai là a String. Nếu bạn chuyển một mảng sang varargs và bạn muốn các phần tử của nó được nhận dạng thành các đối số riêng lẻ và bạn cũng cần thêm một đối số phụ, thì bạn không có lựa chọn nào khác ngoài việc tạo một mảng khác chứa phần tử phụ.

Dưới đây là một số phương pháp trợ giúp hữu ích:

static <T> T[] append(T[] arr, T lastElement) {
    final int N = arr.length;
    arr = java.util.Arrays.copyOf(arr, N+1);
    arr[N] = lastElement;
    return arr;
}
static <T> T[] prepend(T[] arr, T firstElement) {
    final int N = arr.length;
    arr = java.util.Arrays.copyOf(arr, N+1);
    System.arraycopy(arr, 0, arr, 1, N);
    arr[0] = firstElement;
    return arr;
}

Bây giờ bạn có thể làm như sau:

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(append(myArgs, "Z")));
    // prints "[ A ][ B ][ C ][ Z ]"

    System.out.println(ezFormat(prepend(myArgs, "Z")));
    // prints "[ Z ][ A ][ B ][ C ]"

Varargs gotchas # 3: vượt qua một loạt các nguyên thủy

Nó không "hoạt động":

    int[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ [I@13c5982 ]"

Varargs chỉ hoạt động với các loại tham chiếu. Autoboxing không áp dụng cho mảng nguyên thủy. Các công việc sau đây:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"

2
Sử dụng Object ... các tham số matrixdic gây khó khăn cho việc sử dụng chữ ký bổ sung vì trình biên dịch xác định sự không rõ ràng giữa các chữ ký trong đó ngay cả một đối số nguyên thủy có thể được tự động chuyển sang Object.
lươn ghEEz

7
Có một gotcha bị thiếu: nếu phương thức của bạn có một tham số loại Object cuối cùng ... và bạn gọi nó là truyền chuỗi [] chẳng hạn. Trình biên dịch không biết liệu mảng của bạn là mục đầu tiên của varargs hay tương đương với tất cả các varargs. Nó sẽ cảnh báo bạn.
Snicolas

Truyền một đối số kiểu X[]vào một phương thức x(X... xs)đưa ra cảnh báo sau trong Eclipse:Type X[] of the last argument to method x(X...) doesn't exactly match the vararg parameter type. Cast to X[] to confirm the non-varargs invocation, or pass individual arguments of type X for a varargs invocation.
Luke Hutchison

23

Bạn có thể vượt qua một mảng - thực tế nó cũng tương tự như vậy

String.format("%s %s", "hello", "world!");

giống như

String.format("%s %s", new Object[] { "hello", "world!"});

Đó chỉ là đường cú pháp - trình biên dịch chuyển đổi cái đầu tiên thành cái thứ hai, vì phương thức cơ bản đang mong đợi một mảng cho tham số vararg .

Xem


4

jasonmp85 là đúng về việc chuyển một mảng khác đến String.format. Kích thước của một mảng không thể thay đổi một khi được xây dựng, vì vậy bạn phải vượt qua một mảng mới thay vì sửa đổi mảng hiện có.

Object newArgs = new Object[args.length+1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = extraVar; 
String.format(format, extraVar, args);

1

Tôi đã có vấn đề tương tự.

String[] arr= new String[] { "A", "B", "C" };
Object obj = arr;

Và sau đó thông qua obj như đối số varargs. Nó đã làm việc.


2
Xin vui lòng thêm điều đó.
Mathews Nắng
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.