Posso passar uma matriz como argumentos para um método com argumentos variáveis ​​em Java?

276

Eu gostaria de poder criar uma função como:

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

O problema aqui é que argsé tratado como Object[]no método myFormat, e, portanto, é um único argumento para String.format, enquanto eu gostaria cada Objectno argsa ser passado como um novo argumento. Como String.formattambém é um método com argumentos variáveis, isso deve ser possível.

Se isso não for possível, existe um método como String.format(String format, Object[] args)? Nesse caso, eu poderia começar extraVara argsusar uma nova matriz e passá-la para esse método.

user362382
fonte
8
Não posso deixar de me perguntar por que essa pergunta é do tipo "isso é válido"? Você não poderia ter tentado? Não exagere, você fará mais mal do que bem.
Kamasheto 27/05
21
verdade, isso poderia ter sido facilmente testado. no entanto, o bom de uma pergunta como essa é que ela expõe o tópico e solicita respostas interessantes.
akf 28/05
2
Na verdade, eu tentei o código acima, com a intenção de passar os argumentos da matriz como argumentos para o método No entanto, eu não percebi que deveria ter acrescentado extraVar ao args primeiro. Quando você sabe que argumentos variáveis ​​são tratados como uma matriz (mesmo fora do método), isso é bastante lógico.
User362382

Respostas:

181

O tipo subjacente de um método variadic function(Object... args) é function(Object[] args) . A Sun adicionou varargs dessa maneira para preservar a compatibilidade com versões anteriores.

Portanto, você deve apenas ser capaz de preceder extraVara argse chamada String.format(format, args).

jasonmp85
fonte
1
Passar um argumento do tipo X[]para um método x(X... xs)gera o seguinte aviso no 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

Sim, a T...é apenas um açúcar sintático para a T[].

Parâmetros de formato JLS 8.4.1

O último parâmetro formal em uma lista é especial; pode ser um parâmetro de variável arity , indicado por reticências após o tipo.

Se o último parâmetro formal for um parâmetro de variável arity do tipo T , considerar-se-á definir um parâmetro formal do tipo T[]. O método é então um método de variável variável . Caso contrário, é um método de aridade fixo . As invocações de um método de variável arity podem conter expressões de argumento mais reais do que parâmetros formais. Todas as expressões de argumento reais que não correspondem aos parâmetros formais que precedem o parâmetro variável arity serão avaliadas e os resultados armazenados em uma matriz que será passada para a chamada do método.

Aqui está um exemplo para ilustrar:

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 ]"
}

E sim, o acima main método é válido, porque, novamente, String...é justo String[]. Além disso, como as matrizes são covariantes, a String[]é uma Object[], então você também pode chamar de ezFormat(args)qualquer maneira.

Veja também


Dicas Varargs # 1: passagem null

Como os varargs são resolvidos é bastante complicado e, às vezes, faz coisas que podem surpreendê-lo.

Considere este exemplo:

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!!!

Devido à forma como varargs são resolvidos, os últimos invoca declaração com objs = null, o que naturalmente causariam NullPointerExceptioncom objs.length. Se você deseja atribuir um nullargumento a um parâmetro varargs, pode fazer o seguinte:

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

Perguntas relacionadas

A seguir, é apresentado um exemplo de algumas das perguntas que as pessoas fizeram ao lidar com varargs:


Vararg dicas # 2: adicionando argumentos extras

Como você descobriu, o seguinte não "funciona":

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

Devido à maneira como os varargs funcionam, ezFormatna verdade obtém 2 argumentos, sendo o primeiro a String[], o segundo sendo a String. Se você estiver passando uma matriz para varargs e quiser que seus elementos sejam reconhecidos como argumentos individuais, e também precisar adicionar um argumento extra, não terá outra opção a não ser criar outra matriz que acomode o elemento extra.

Aqui estão alguns métodos auxiliares úteis:

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;
}

Agora você pode fazer o seguinte:

    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 ]"

Dicas de Varargs # 3: passando uma matriz de primitivas

Não "funciona":

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

Varargs funciona apenas com tipos de referência. A caixa automática não se aplica à matriz de primitivas. Os seguintes trabalhos:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
poligenelubricants
fonte
2
O uso de parâmetros variáveis ​​do Object ... dificulta o uso de assinaturas adicionais, pois o compilador identifica a ambiguidade entre as assinaturas, onde mesmo um argumento primitivo pode ser autoboxado para o Object.
enguia ghEEz
7
Existe uma pegadinha ausente: se o seu método tiver um último parâmetro do tipo Objeto ... e você o chamará passando um String [] por exemplo. O compilador não sabe se sua matriz é o primeiro item das varargs ou é o equivalente a todas as varargs. Isso irá avisá-lo.
Snicolas
Passar um argumento do tipo X[]para um método x(X... xs)gera o seguinte aviso no 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

Não há problema em passar uma matriz - na verdade, é a mesma coisa

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

é o mesmo que

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

É apenas açúcar sintático - o compilador converte o primeiro no segundo, já que o método subjacente espera uma matriz para o parâmetro vararg .

Vejo

mdma
fonte
4

jasonmp85 está certo em passar uma matriz diferente para String.format. O tamanho de uma matriz não pode ser alterado uma vez construído, então você precisa passar uma nova matriz em vez de modificar a existente.

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

Eu estava tendo o mesmo problema.

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

E depois passou o obj como argumento varargs. Funcionou.

Srijit Paul
fonte
2
Por favor, adicione isso também.
perfil completo de Mathews Sunny