Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generic varargs method parameters

I am wondering if there is an easy, elegant and reusable way to pass a string and a string array to a method that expect varargs.

/**
 * 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
}

Even if all the types are consistent (String) I cannot find a way to tell to the JVM to flatten p2 and inject it to the merged parameter list?

At this point the only way is to create a new array, copy everything into it and pass it to the function.

Any idea?


EDIT

Base on your proposal here is the generic method I'll use:

/**
 * 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");
like image 643
poussma Avatar asked Mar 12 '13 09:03

poussma


People also ask

What is Varargs parameter in Java?

Variable Arguments (Varargs) in Java is a method that takes a variable number of arguments. Variable Arguments in Java simplifies the creation of methods that need to take a variable number of arguments.

How many varargs parameters can a method have?

Each method can only have one varargs parameter. The varargs argument must be the last parameter.

What is the rule for using varargs in Java?

Rules to follow while using varargs in JavaWe can have only one variable argument per method. If you try to use more than one variable arguments a compile time error is generated.

What symbol is used for a Varargs method parameter?

In Java, an argument of a method can accept arbitrary number of values. This argument that can accept variable number of values is called varargs. In order to define vararg, ... (three dots) is used in the formal parameter of a method.


2 Answers

I don't think there is a way to have the scalar and the array implicitly flattened into a single array.

The cleanest solution I can think of is to use a helper function:

// 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));
}
like image 93
NPE Avatar answered Oct 18 '22 02:10

NPE


Changing the signature of merge to:

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

Will at least allow you to call merge without problems. You would have to deal with arrays of string as a parameter, though.

like image 27
Sebastian Avatar answered Oct 18 '22 04:10

Sebastian