Подтвердить что ты не робот

Параметры метода varargs для Java

Мне интересно, есть ли простой, элегантный и многоразовый способ передать строку и строковый массив методу, который ожидает 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
}

Даже если все типы согласованы (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");
4b9b3361

Ответ 1

Я не думаю, что есть способ иметь скаляр, а массив неявно сглажен в один массив.

Самое чистое решение, о котором я могу думать, это использовать вспомогательную функцию:

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

Ответ 2

Изменение подписи merge на:

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

По крайней мере позволит вам вызвать слияние без проблем. Однако вам придется иметь дело с массивами строки в качестве параметра.

Ответ 3

Я думаю, что создание нового массива - путь.

Например, вы можете использовать ArrayUtils из Apache Commons для достижения этого.

ArrayUtils.addAll(p2, p1);

С уважением, Томас

Ответ 4

Внутри separated параметр p2 равен String[]. Метод merged может принимать либо один параметр String[], либо последовательность строк, но не смесь этих двух, поэтому я не думаю, что у вас есть много вариантов, кроме как построить новый массив (это то, что делает varargs за кулисами).

Ответ 5

Из varargs doc

Три периода после окончательного типа параметра показывают, что окончательный аргумент может быть передан как массив или как последовательность Аргументы.

В нем четко указано an array OR a sequence of arguments, поэтому вам нужно сгладить массив самостоятельно, например, как было предложено NPE.

Ответ 6

Я сомневаюсь, что вы можете это сделать, компилятор Java должен перевести слияние (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 )
}

Массивы из стандартного JDK, Lists - из Guava. Оба метода asList() создают представления над выделенными массивами, а не копией, поэтому проблем с памятью и памятью не так много.

Я не думаю, что существует более простой подход. Да, копирование в новый массив, как это было предложено другими, проще, но время/память потребляют. Если ваши массивы невелики, может случиться так, что создание и доступ к спискам может быть медленнее, чем копирование, но для больших массивов копирование станет более накладным.

Мне нравятся предложения Себастьяна: вы определяете оболочку слияния(), которая выравнивает массив в полете. Это работает хорошо, пока merge() не является сторонним методом, и вы можете его изменить. Иначе я делаю что-то подобное здесь.