18

我想知道是否有一种简单、优雅且可重用的方法将字符串和字符串数组传递给期望可变参数的方法。

/**
 * The entry point with a clearly separated list of parameters.
 */
public void separated(String p1, String ... p2) {
    merged(p1, p2, "another string", new String[]{"and", "those", "one"});
}

/**
 * For instance, this method outputs all the parameters.
 */
public void merged(String ... p) {
    // magic trick
}

即使所有类型都是一致的(String),我也找不到告诉 JVM 将p2展平并将其注入合并参数列表的方法?

此时唯一的方法是创建一个新数组,将所有内容复制到其中并将其传递给函数。

任何想法?


编辑

根据您的建议,这里是我将使用的通用方法:

/**
 * Merge the T and T[] parameters into a new array.
 *
 * @param type       the destination array type
 * @param parameters the parameters to merge
 * @param <T>        Any type
 * @return the new holder
 */
@SuppressWarnings("unchecked")
public static <T> T[] normalize(Class<T> type, Object... parameters) {
    List<T> flatten = new ArrayList<>();
    for (Object p : parameters) {
        if (p == null) {
            // hum... assume it is a single element
            flatten.add(null);
            continue;
        }
        if (type.isInstance(p)) {
            flatten.add((T) p);
            continue;
        }
        if (p.getClass().isArray() && 
            p.getClass().getComponentType().equals(type)) {
            Collections.addAll(flatten, (T[]) p);
        } else {
            throw new RuntimeException("should be " + type.getName() + 
                                             " or " + type.getName() + 
                                             "[] but was " + p.getClass());
        }
    }
    return flatten.toArray((T[]) Array.newInstance(type, flatten.size()));
}

normalize(String.class, "1", "2", new String[]{"3", "4", null}, null, "7", "8");
4

6 回答 6

6

我认为没有办法将标量和数组隐式展平为单个数组。

我能想到的最干净的解决方案是使用辅助函数:

// generic helper function
public static<T> T[] join(T scalar, T[] arr) {
    T[] ret = Arrays.copyOfRange(arr, 0, arr.length + 1);
    System.arraycopy(ret, 0, ret, 1, arr.length);
    ret[0] = scalar;
    return ret;
}

public void separated(String p1, String ... p2) {
    merged(join(p1, p2));
}
于 2013-03-12T09:41:36.590 回答
2

将签名更改merge为:

public<T> void merged(T ... p) 

至少可以让您毫无问题地调用合并。但是,您必须将字符串数组作为参数处理。

于 2013-03-12T09:41:39.930 回答
0

我认为创建一个新数组是要走的路。

例如,您可以使用ArrayUtilsApache Commons来实现这一目标。

ArrayUtils.addAll(p2, p1);

最好的问候,托马斯

于 2013-03-12T09:40:46.140 回答
0

我怀疑你能做到这一点,Java 编译器应该将 merge(p1, p2) 转换为合并的 ( p1, p2 [ 0 ], p2 [ 1 ] ... p2 [ p2.length ] ),但我不认为他们曾经想创建这样一个智能编译器(如果合并(p1,p2)也存在,它也会产生问题)。

您可以这样做(使用伪语法):

import com.google.common.collect.Lists
import java.util.Arrays

separated ( String p1, String... p2 ) {
  merged ( Lists.asList ( p1, p2 ) )
}

merged ( List<String> p ) {
...
}

merged ( String ... p ) {
  merged ( Arrays.asList ( p )
}

Arrays 来自标准 JDK,Lists来自 Guava。两个 asList() 方法都在下划线数组上创建视图,而不是副本,因此没有太多速度/内存问题。

我认为不存在更简单的方法。是的,正如其他人所建议的那样,复制到一个新数组中更简单,但会耗费时间/内存。如果您的数组很小,创建和访问列表可能会比复制慢,但对于较大的数组,复制将成为更高的开销。

我喜欢塞巴斯蒂安的建议:您定义了一个合并()的包装器,它可以在飞行中展平数组。只要 merge() 不是第 3 方方法并且您可以更改它,这就会很好地工作。否则,我在这里做类似的事情。

于 2013-03-12T10:07:54.670 回答
0

在 内separated,参数p2是一个String[]。该merged方法可以采用单个参数String[]字符串序列,但不能同时采用两者,所以我认为除了构建一个新数组(这就是可变参数在幕后所做的)之外,您没有太多选择。

于 2013-03-12T09:46:55.290 回答
0

来自可变参数文档

最后一个参数类型后面的三个句点表示最后一个参数可以作为数组或参数序列传递。

它明确指出an array OR a sequence of arguments,因此您必须自己展平阵列,例如 NPE 所建议的。

于 2013-03-12T09:47:16.973 回答