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

Varargs в перегрузке метода в Java

Следующий код не компилируется.

package varargspkg;

public class Main {

    public static void test(int... i) {
        for (int t = 0; t < i.length; t++) {
            System.out.println(i[t]);
        }

        System.out.println("int");
    }

    public static void test(float... f) {
        for (int t = 0; t < f.length; t++) {
            System.out.println(f[t]);
        }

        System.out.println("float");
    }

    public static void main(String[] args) {
        test(1, 2);  //Compilation error here quoted as follows.
    }
}

Выдается ошибка времени компиляции.

ссылка на тест неоднозначна, оба метода test (int...) в varargspkg.Main и метод test (float...) в varargspkg.Main match

Кажется очевидным, потому что значения параметров в вызове метода test(1, 2); можно повысить до int, а также float

Если кто-либо или оба параметра имеют суффикс F или F, он компилируется.


Если мы, однако, представляем принимающие параметры в сигнатуре метода с соответствующими типами обертки следующим образом

public static void test(Integer... i) {
    System.out.println("Integer" + Arrays.asList(i));
}

public static void test(Float... f) {
    System.out.println("Float" + Arrays.asList(f));
}

то вызов метода test(1, 2); не вызывает ошибок компиляции. Метод, который должен быть вызван в этом случае, тот, который принимает один параметр Integer varargs (первый в предыдущем фрагменте).

Почему в этом случае ошибка, как в первом случае, не сообщается? По-видимому, здесь применяются автоматическое боксирование и автоматическое продвижение по типу. Сначала применяется автоматический бокс, чтобы разрешить ошибку?

В документах Oracle говорится:

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

Последнее предложение в этой ссылке. Это, однако, для лучшего понимания varargs.

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

public class OverLoading {

    public static void main(String[] args) {
        load(1);
    }

    public static void load(int i) {
        System.out.println("int");
    }

    public static void load(float i) {
        System.out.println("float");
    }
}

EDIT:

Ниже приведена snap shot, которая указывает на ошибку компиляции. Я создал новое приложение, поэтому имя пакета отличается.

введите описание изображения здесь

Я использую JDK 6.

4b9b3361

Ответ 1

Вы можете либо Widen, либо Box, но вы не можете обойти оба, если только вы не находите boxing and widening до Object (An int Integer (Boxing), а затем Integer to Object (Расширение) является законным, поскольку каждый class является подклассом Object, поэтому для Integer можно передать параметр Object)

Аналогично, int to Number также является законным (int → Integer → Number) Поскольку Number является супер классом Integer, это возможно.

Посмотрите это в своем примере: -

public static void test(Integer...i)

public static void test(Float...f)

При выборе того, какой перегруженный метод выбрать, когда используются бокс, расширение и вар-args, есть несколько правил: -

  • Примитивное расширение использует аргумент метода smallest
  • Тип обтекателя не может быть расширен до другого типа Wrapper
  • Вы можете вставить из int в Integer и расширить до Object, но не до Long
  • Расширение бьет Бокс, Бокс побеждает Вар-Аргса.
  • Вы можете вставить поле, а затем Widen (An int может стать Object через Integer)
  • Вы не можете Widen, а затем Box (An int не может стать Long)
  • Вы не можете комбинировать var-args с расширением или боксом

Итак, основываясь на приведенных выше правилах: -

Когда вы передаете два целых числа в вышеуказанные функции,

  • согласно правилу 3, он должен быть первым Widened, а затем Boxed, чтобы вписаться в Long, что является незаконным в соответствии с правилом 5 (вы не можете Widen, а затем Box).
  • Итак, Boxed хранится в Integer var-args.

Но в первом случае, когда у вас есть методы с var-args примитивных типов: -

public static void test(int...i)
public static void test(float...f)

Тогда test(1, 2) может вызывать оба метода (поскольку ни один из них более подходит для применения rule 1): -

  • В первом случае это будет var-args
  • Во втором случае это будет Widening, а затем Var-args (что разрешено)

Теперь, когда у вас есть методы с ровно одним int и одним flost: -

public static void test(int i)
public static void test(float f)

Затем при вызове с использованием test(1) выполняется правило 1, и выбирается наименьшее возможное расширение (т.е. int, где вообще не требуется расширения). Таким образом, вызывается 1-й метод.

Для получения дополнительной информации вы можете обратиться к JLS - Method Invocation Conversion

Ответ 2

В Java 1 описывается как int. Он может быть либо автоматически помещен в экземпляр Integer, либо переведен на float, и это объясняет, почему компилятор не может решить, какой метод он должен вызывать. Но он никогда не будет автоматически помещен в поле Long или float (или любой другой тип).

С другой стороны, если вы пишете 1F, это представление float, которое может быть автоматически загружено в float (и, в том же духе, никогда не будет автоматически загружено на Integer или что-либо еще).

Ответ 3

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

When you write 1,2 
     -> it can be be both int[] or float[] and hence the issue being complained.

When you write 1,2F 
     -> it can be be only float[] and hence the NO issue being complained.

То же самое с другими двумя опциями i.e.

When you write 1F,2 
     -> it can be be only float[] and hence the NO issue being complained.

When you write 1F,2F 
     -> it can be be only float[] and hence the NO issue being complained.

С другой стороны, когда вы используете int или float, нет экземпляра типа переменной. Когда вы используете 1, он сначала пытается найти метод с int в качестве аргумента, если нет, он продвигает тип и идентифицирует метод с поплавком. Если оба метода доступны, сначала будет использоваться int.

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

Ответ 4

Почему в этом случае ошибка, как в первом случае, не сообщается? По-видимому, здесь применяются автоматическое боксирование и автоматическое продвижение по типу. Сначала применяется автоматическое боксирование, ошибка устранена?

Просто мнение - в случае varargs JVM на самом деле должен создать массив аргументов. В случае Integer и Float очевидно, какой тип массива он должен создать. Таким образом, вероятно, это может быть причиной ошибки двусмысленности.

Но все-таки это путано, почему он не может создать массив целых чисел, когда по умолчанию 1, 3 являются ints.

Похоже, это обсуждалось здесь в SO в прошлом ошибка с varargs и перегрузка? и является infact ошибка, в соответствии с обсуждением.