我可以将数组作为参数传递给 Java 中具有可变参数的方法吗?

发布于 2024-09-02 22:04:15 字数 577 浏览 7 评论 0原文

我希望能够创建一个如下函数:

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

这里的问题是 args 在方法 myFormat 中被视为 Object[] ,因此是 String.format 的单个参数,而我希望将 args 中的每个 Object 作为新参数传递。由于 String.format 也是一个带有可变参数的方法,所以这应该是可能的。

如果这是不可能的,是否有类似 String.format(String format, Object[] args) 的方法?在这种情况下,我可以使用新数组将 extraVar 附加到 args 并将其传递给该方法。

I'd like to be able to create a function like:

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

The problem here is that args is treated as Object[] in the method myFormat, and thus is a single argument to String.format, while I'd like every single Object in args to be passed as a new argument. Since String.format is also a method with variable arguments, this should be possible.

If this is not possible, is there a method like String.format(String format, Object[] args)? In that case I could prepend extraVar to args using a new array and pass it to that method.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

痞味浪人 2024-09-09 22:04:15

是的,T... 只是 T[] 的语法糖。

JLS 8.4.1 格式参数< /a>

列表中的最后一个形式参数是特殊的;它可能是一个变量数量参数,由类型后面的省略号指示。

如果最后一个形参是T类型的可变参数,则认为定义了T[]类型的形参。该方法是一个可变参数方法。否则,它是一个固定数量方法。变量数量方法的调用可能包含比形式参数更多的实际参数表达式。所有与变量参数前面的形式参数不对应的实际参数表达式都将被计算,并将结果存储到一个数组中,该数组将传递给方法调用。

下面是一个例子来说明:

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

是的,上面的 main 方法是有效的,因为 String... 只是 String[]。此外,由于数组是协变的,String[]Object[],因此您也可以以任何一种方式调用 ezFormat(args)

另请参阅


#1: 传递 null

可变参数的解析是相当复杂的,有时它会做一些让你惊讶的事情。

考虑这个例子:

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

由于可变参数的解析方式,最后一个语句使用 objs = null 进行调用,这当然会导致 NullPointerExceptionobjs.length >。如果您想为 varargs 参数提供一个 null 参数,您可以执行以下任一操作:

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

相关问题

以下是人们在处理 varargs 时提出的一些问题的示例:


Vararg 陷阱 #2:添加额外参数

正如您所发现的出来,以下内容不起作用:

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

由于可变参数的工作方式,ezFormat 实际上获取 2 个参数,第一个是 String[],第二个是字符串。如果您将数组传递给 varargs,并且希望其元素被识别为单独的参数,并且还需要添加额外的参数,那么您别无选择,只能创建另一个数组容纳额外的元素。

以下是一些有用的辅助方法:

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

现在您可以执行以下操作:

    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 陷阱#3:传递基元数组

它不起作用:

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

Varargs 仅适用于引用类型。自动装箱不适用于基元数组。以下作品:

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

Yes, a T... is only a syntactic sugar for a T[].

JLS 8.4.1 Format parameters

The last formal parameter in a list is special; it may be a variable arity parameter, indicated by an elipsis following the type.

If the last formal parameter is a variable arity parameter of type T, it is considered to define a formal parameter of type T[]. The method is then a variable arity method. Otherwise, it is a fixed arity method. Invocations of a variable arity method may contain more actual argument expressions than formal parameters. All the actual argument expressions that do not correspond to the formal parameters preceding the variable arity parameter will be evaluated and the results stored into an array that will be passed to the method invocation.

Here's an example to illustrate:

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

And yes, the above main method is valid, because again, String... is just String[]. Also, because arrays are covariant, a String[] is an Object[], so you can also call ezFormat(args) either way.

See also


Varargs gotchas #1: passing null

How varargs are resolved is quite complicated, and sometimes it does things that may surprise you.

Consider this example:

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

Due to how varargs are resolved, the last statement invokes with objs = null, which of course would cause NullPointerException with objs.length. If you want to give one null argument to a varargs parameter, you can do either of the following:

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

Related questions

The following is a sample of some of the questions people have asked when dealing with varargs:


Vararg gotchas #2: adding extra arguments

As you've found out, the following doesn't "work":

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

Because of the way varargs work, ezFormat actually gets 2 arguments, the first being a String[], the second being a String. If you're passing an array to varargs, and you want its elements to be recognized as individual arguments, and you also need to add an extra argument, then you have no choice but to create another array that accommodates the extra element.

Here are some useful helper methods:

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

Now you can do the following:

    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: passing an array of primitives

It doesn't "work":

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

Varargs only works with reference types. Autoboxing does not apply to array of primitives. The following works:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
青丝拂面 2024-09-09 22:04:15

可变参数方法的基础类型 function(Object... args) 函数(Object[] args)。 Sun 以这种方式添加可变参数以保持向后兼容性。

因此,您应该能够在 args 前面添加 extraVar 并调用 String.format(format, args)

The underlying type of a variadic method function(Object... args) is function(Object[] args). Sun added varargs in this manner to preserve backwards compatibility.

So you should just be able to prepend extraVar to args and call String.format(format, args).

白昼 2024-09-09 22:04:15

传递一个数组是可以的 - 事实上它相当于相同的东西

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

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

它只是语法糖 - 编译器将第一个转换为第二个,因为底层方法需要 vararg 参数。

请参阅

It's ok to pass an array - in fact it amounts to the same thing

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

is the same as

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

It's just syntactic sugar - the compiler converts the first one into the second, since the underlying method is expecting an array for the vararg parameter.

See

傾旎 2024-09-09 22:04:15

jasonmp85 关于将不同的数组传递给 String.format 的说法是正确的。数组的大小一旦构造就无法更改,因此您必须传递一个新数组而不是修改现有数组。

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

jasonmp85 is right about passing a different array to String.format. The size of an array can't be changed once constructed, so you'd have to pass a new array instead of modifying the existing one.

Object newArgs = new Object[args.length+1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = extraVar; 
String.format(format, extraVar, args);
宁愿没拥抱 2024-09-09 22:04:15

我有同样的问题。

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

然后将 obj 作为 varargs 参数传递。
它起作用了。

I was having same issue.

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

And then passed the obj as varargs argument.
It worked.

傲鸠 2024-09-09 22:04:15

除了 @genelubricant 的#2 - 如果只读就足够了,并且如果您想付出一些额外的努力来获得 List 的一些便利 - 不一定需要复制数组并包装它,加上在访问时在任意索引处注入所需的额外值,可以这样实现:

public static <T> List<Object> wrappedUnmodfiableListInsertion(final T insert, final Integer insertIdx, final T... source) {
  final boolean isInsertion = insertIdx != null;
  return new AbstractList<Object>() {
    @Override
    public int size() {
      return source != null ? isInsertion ? source.length + 1 : source.length : 0;
    }
    @Override
    public Object get(int index) {
      if (source == null) {
        if (isInsertion) {
          return index == insertIdx && index == 0 ? insert : null;
        }
        return EMPTY_LIST;
      } 
      if (isInsertion) {
        if (index == insertIdx) {
          return insert;
        } 
        return source[insertIdx > index ? index : index - 1];
      }
      return source[index];
    }
  };
}

In addition to #2 of @genelubricant - in case read-only is enough and if you want to swap a little extra effort for gaining some of the conveniences of a List - copying the array is not necessarily required and wrapping it, plus injecting the desired extra value at an arbitrary index at access time, can be achieved like this:

public static <T> List<Object> wrappedUnmodfiableListInsertion(final T insert, final Integer insertIdx, final T... source) {
  final boolean isInsertion = insertIdx != null;
  return new AbstractList<Object>() {
    @Override
    public int size() {
      return source != null ? isInsertion ? source.length + 1 : source.length : 0;
    }
    @Override
    public Object get(int index) {
      if (source == null) {
        if (isInsertion) {
          return index == insertIdx && index == 0 ? insert : null;
        }
        return EMPTY_LIST;
      } 
      if (isInsertion) {
        if (index == insertIdx) {
          return insert;
        } 
        return source[insertIdx > index ? index : index - 1];
      }
      return source[index];
    }
  };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文