Java два varargs в одном методе

есть ли способ в java создать метод, который ожидает два разных varargs? Я знаю, с тем же типом объекта это невозможно, потому что компилятор не знает, с чего начать или закончить. Но почему это также невозможно с различными типами объектов?

например:

public void doSomething(String... s, int... i){
    //...
    //...
}

есть ли способ создать такой метод? Спасибо!

12 ответов


только один переменным числом аргументов, к сожалению. Но использование asList () делает его почти таким же удобным:

 public void myMethod(List<Integer> args1, List<Integer> args2) {
   ...
 }

 -----------

 import static java.util.Arrays.asList;
 myMethod(asList(1,2,3), asList(4,5,6));

в Java, только один разрешено, и он должен быть последним параметром подписи.

но все это все равно преобразует его в массив, поэтому вы должны просто сделать свои два параметра явными массивами:

public void doSomething(String[] s, int[] i){

возможный дизайн API, в котором вызывающий код выглядит как

    doSomething("a", "b").with(1,2);

через" fluent " API

public Intermediary doSomething(String... strings)
{
    return new Intermediary(strings);
}

class Intermediary
{
    ...
    public void with(int... ints)
    {
        reallyDoSomething(strings, ints);
    }
}

void reallyDoSomething(String[] strings, int[] ints)
{
    ...
}

опасность в том, что программист забыл позвонить with(...)

    doSomething("a", "b");  // nothing is done

может быть, это немного лучше

    with("a", "b").and(1, 2).doSomething();

единственный vararg разрешено. Это потому, что несколько vararg аргументы неоднозначны. Например, что делать, если вы прошли в двух varargs одного класса?

public void doSomething(String...args1, String...args2);

где заканчивается args1 и начинается args2? Или как насчет чего-то более запутанного.

class SuperClass{}
class ChildClass extends SuperClass{}
public void doSomething(SuperClass...args1, ChildClass...args2);

ChildClass расширяет суперкласс, и поэтому может законно существовать в args1 или args2. Эта путаница почему только один varargs разрешено.

varargs должен также отображаться в конце объявление метода.

просто объявите конкретный тип вместо 2 массивов.


хотя такие вещи иногда полезны, обычно, если вы обнаружите, что вы нажмете ограничение в Java, вы, вероятно, можете что-то перепроектировать и выйти намного лучше. Вот несколько возможных других способов взглянуть на это...

Если два списка связаны вообще, вы, вероятно, хотите создать класс-оболочку для двух разных списков и передать оболочку. Обертки вокруг коллекций почти всегда являются хорошей идеей-они дают вам место для добавления кода, который относится в коллекцию.

Если это способ инициализации данных, проанализируйте их из строки. Например, "abc, 123: def, 456: jhi,789" почти смущающе легко разделить с 2 разделенными операторами и циклом (2-3 строками кода). Вы даже можете сделать небольшой пользовательский класс синтаксического анализатора, который анализирует такую строку в структуру, которую вы вводите в свой метод.

Хмм--честно говоря, из-за инициализации данных я даже не знаю, почему вы все равно хотите это сделать, в любом другом случае, и я ожидайте, что вы будете проходить в 2 коллекциях и не будете заинтересованы в varags вообще.


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

public void doSomething(Object... stringOrIntValues) {
    ...
    ...
}

и используйте этот метод так:

doSomething(stringValue1, stringValue2, intValue1, intValue2,         
    intValue3);

Это старый поток, но я думал, что это будет полезно независимо.

решение, которое я нашел, не очень аккуратно, но оно работает. Я создал отдельный класс для тяжелой работы. У него есть только две переменные, которые мне нужны, и их геттеры. Конструктор обрабатывает методы set самостоятельно.

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

public class DataDirectionPair{

    Data dat;
    Directions dir;

    public DataDirectionPair(Data dat, Directions dir) {
        super();
        this.dat = dat;
        this.dir = dir;
    }

    /**
     * @return the node
     */
    public Node getNode() {
        return node;
    }

    /**
     * @return the direction
     */
    public Directions getDir() {
        return dir;
    }
}

Я бы тогда просто передал этот класс как vararg для метода

public void method(DataDirectionPair... ndPair){
    for(DataDirectionPair temp : ndPair){
        this.node = temp.getNode();
        this.direction = temp.getDir();
        //or use it however you want
    }
}

это невозможно, потому что спецификация языка Java говорит Так (см. 8.4.1. Формальные Параметры):

последний формальный параметр метода или конструктора является специальной: она может быть переменный параметр arity, обозначенной многоточием следуя типу.

обратите внимание, что с многоточием (...) является знаком самому себе (§3.11). Можно поместить пробелы между ним и типом, но это уныние вопрос стиля.

если последний формальный параметр является параметром переменной arity, метод это метод переменной arity. В противном случае, это исправлен метод arity.

что касается того, почему только один и только последний параметр, это было бы догадкой, но, вероятно, потому, что это может привести к неразрешимым или неоднозначным проблемам (например, рассмотрим, что происходит с method(String... strings, Object... objects)), и только разрешение непересекающихся типов приведет к осложнения (например, рассмотрение рефакторингов, где ранее непересекающиеся типы внезапно), отсутствие ясности, когда это работает или не работает, и сложность для компилятора, чтобы решить, когда это применимо или нет.


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

вместо того, чтобы заставить разработчика обернуть входной параметр в список или массив, будет полезно использовать подход "карри" или лучше шаблон компоновщика.

рассмотрим следующий код:

/**
 * Just a trivial implementation
 */
public class JavaWithCurry {

    private List<Integer> numbers = new ArrayList<Integer>();
    private List<String> strings = new ArrayList<String>();

    public JavaWithCurry doSomething(int n) {

        numbers.add(n);

        return this;
    }

    public JavaWithCurry doSomething(String s) {

        strings.add(s);

        return this;

    }

    public void result() {

        int sum = -1;

        for (int n : numbers) {
            sum += n;
        }


        StringBuilder out = new StringBuilder();

        for (String s : strings) {

            out.append(s).append(" ");

        }

        System.out.println(out.toString() + sum);

    }

    public static void main(String[] args) {

        JavaWithCurry jwc = new JavaWithCurry();

        jwc.doSomething(1)
                .doSomething(2)
                .doSomething(3)
                .doSomething(4)
                .doSomething(5)
                .doSomething("a")
                .doSomething("b")
                .doSomething("c")
                .result();

    }

}

Как вы можете видеть, вы можете добавить новые элементы какой тип вам нужен, когда вам нужно.

вся реализация завернута.


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

public void doSomething(int... i){
    doSomething(new String[0], i);
}
public void doSomething(String s, int... i){
    doSomething(new String[]{ s }, i);
}
public void doSomething(String s1, String s2, int... i){
    doSomething(new String[]{ s1, s2 }, i);
}
public void doSomething(String s1, String s2, String s3, int... i){
    doSomething(new String[]{ s1, s2, s3 }, i);
}
public void doSomething(String[] s, int... i) {
    // ...
    // ...
}

follwing на Лемюэле Адане (не могу прокомментировать сообщение, из - за отсутствия rep :))

Если вы используете

public void f(Object... args){}

затем вы можете использовать цикл как определить класс объекта (в Java)?

например

{
   int i = 0;
   while(i< args.length && args[i] instanceof String){
         System.out.println((String) args[i]);
         i++ ;
   }
   int sum = 0;
   while(i< args.length){
         sum += (int) args[i];
         i++ ;
   }
   System.out.println(sum);
}

или все, что вы намерены делать.


вы можете конвертировать ваши varargs в массивы

public void doSomething(String[] s, int[] i) {
    ...
}

затем с помощью некоторых вспомогательных методов преобразовать varargs в массив, как это:

public static int[] intsAsArray(int... ints) {
    return ints;
}

public static <T> T[] asArray(T... ts) {
    return ts;
}

затем вы можете использовать эти вспомогательные методы для преобразования параметров vararged.

doSomething(asArray("a", "b", "c", "d"), intsAsArray(1, 2, 3));