Как я могу расширить аргументы в Java? - PullRequest
6 голосов
/ 16 февраля 2012

Следующий фрагмент кода Python делает именно то, что я имею в виду:

def function(a, b, c):
    print("%i :: %s :: %f" % (a,b,c))

a = 1
b = "abc"
c = 1.0

function(a, b, c)

list = [a, b, c]

# This is what I am searching for in Java
function(*(list))

Итак, у меня есть одна структура, похожая на список, и я не знаю, сколько у нее аргументов, но я знаю, что у нее правильное количество аргументов с правильным типом и форматом. И я хочу передать их методу. Поэтому я должен «расширить» эти аргументы. Кто-нибудь знает, как это сделать в Java?

Ответы [ 6 ]

5 голосов
/ 16 февраля 2012

Java не имеет этой возможности, поскольку в качестве статически строго типизированного языка метод редко принимает коллекцию значений одинакового типа, которые могут быть сохранены в каком-либо составном объекте (массив, список и т. Д.). ). У Python нет этой проблемы, потому что все динамически типизировано. Вы можете сделать это в Java, определив вспомогательный метод, который принимает один объект для хранения всех параметров, а затем расширяет их при вызове реального метода.

Надеюсь, это поможет!

3 голосов
/ 16 февраля 2012

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

public void method1(String p1, Integer p2) {
}

public void method2(Object... params) {
    method1((String)params[0], (Integer)params[1];
}

Вы можете позвонить method2 как:

method2("abc", 1);

Конечно, этот пример немного избыточен, потому что вы можете напрямую вызвать method1, но это не то, что я хотел проиллюстрировать. :)

1 голос
/ 16 февраля 2012

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

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

0 голосов
/ 16 февраля 2012

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

SomeType foo = //obtained from elsewhere
List<?> args = //obtained from somewhere else

//this bit obtains the method handle
Method theMethod = null;
for (Method m : foo.getClass().getMethods()) {
    if (m.getName().equals("theMethodIWant") {
        theMethod = m;
        break;
    }
}

theMethod.invoke(foo, args.toArray()); //this is the line that does the magic

Некоторые довольно серьезные предостережения:

  • Отражение может оказать значительное влияние на производительность по сравнению с прямым вызовом - гораздо больше, чем в динамическом языке, таком как python.Это может иметь или не иметь значения.Если сомневаетесь, измерьте.

  • Блок, который получает дескриптор метода, приведет к нулевому указателю, если метод с таким именем не найден

  • Список аргументов должен иметь точно правильное количество аргументов с правильным типом.Вы несете ответственность за проверку этого где-то еще.Это сложнее, чем это было бы в динамическом языке, так как среда выполнения сделает для вас намного меньше (если таковые имеются) приведения типов.

  • method.invoke () может вызвать хостисключений (IllegalAccessException, IllegalArgumentException, InvocationTargetException и т. д.), которые этот пример игнорирует.Вам придется соответствующим образом отлавливать и обрабатывать эти ошибки

  • , если вызываемый метод не является общедоступным, вам придется проделать еще большую работу, чем эта, чтобы вызвать его.

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

В общем, я бы порекомендовал не использовать рефлексию, если это возможно.

0 голосов
/ 16 февраля 2012

Я думаю, что это то же самое, что и ваш исходный код с похожим синтаксисом ...

public class MultiArgs {

    public static void main(String[] args) {
        int a = 1;
        String b = "abc";
        double c = 1.0;
        List<?> list = Arrays.asList(a, b, c);

        System.out.println(function(a, b, c)); // 1 :: abc :: 1.000000
        System.out.println(function(list.toArray())); // 1 :: abc :: 1.000000
    }

    private static <T extends Object> String function(T... objects) {
        return String.format("%d :: %s :: %f", objects);
    }
}
0 голосов
/ 16 февраля 2012

Вы можете сделать это:

import java.util.Formatter;

// ...

StringBuilder target = new StringBuilder();
Formatter formatter = new Formatter(target);

List<Integer> integers = new ArrayList<Integer>();
integers.add(1);
integers.add(2);
integers.add(3);

formatter.format("%d %d %d", integers.toArray());
System.out.println(target.toString());

Редактировать: Javadoc для java.util.Formatter

...