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.
Ответы
Ответ 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 ошибка, в соответствии с обсуждением.