Перегрузка метода переменными аргументами (varargs)
Я удивлен, увидев вывод этого кода:
public class File
{
public static void main(String[] args)
{
movie();
}
static void movie(double... x)
{
System.out.println("No varargs");
}
static void movie(int... x)
{
System.out.println("One argument");
}
}
Он выводит,
One argument
Почему это так?
Я думал, что этот код не будет компилироваться, потому что вызов movie()
является неоднозначным, но он отлично работает и выдает One argument
.
Если я изменю код на:
public class File
{
public static void main(String[] args)
{
movie();
}
static void movie(boolean... x) //Changed the parameter type to boolean from double
{
System.out.println("No varargs");
}
static void movie(int... x)
{
System.out.println("One argument");
}
}
Появляется сообщение об ошибке.
Почему первый код работает нормально, а второй дает ошибку?
Ответы
Ответ 1
Это связано с тем, что int
более специфичен, чем double
, в то время как сравнение между int
и boolean
отсутствует.
Как указано в разделе JLS 15.12.2.5 (выделено мной):
Один применимый метод m1 более конкретный, чем другой применимый метод m2, для вызова с выражениями аргументов e1,..., ek, если выполнено одно из следующих утверждений:
- ...
- m2 не является общим, а m1 и m2 применимы при вызове переменной arity, и где первые k переменных параметров arty-параметров m1 являются S1,..., Sk и первой переменной k типы параметров arty m2 являются T1,..., Tk, тип Si более специфичен, чем Ti для аргумента ei для всех я (1 ≤ я ≤ k). Кроме того, если m2 имеет k + 1 параметров, то k + 1'th тип параметра переменной arity m1 является подтипом k + 1'th переменной параметра arity параметра m2.
Что более конкретное на самом деле означает, позже определяется подтипирование:
Тип S более специфичен, чем тип T для любого выражения, если S <: T.
Это означает, что S
более конкретный, чем T
is S
является подтипом T
. Для примитивных типов это сводится к следующим свойствам:
- double > float
- float > long
- long > int
- int > char
- int > short
- short > byte
Обратите внимание, что boolean
не существует.
Таким образом,
public static void main(String[] args) {
movie();
}
static void movie(int... x) { }
static void movie(short... x) { }
static void movie(double... x) { }
static void movie(byte... x) { }
компилируется и movie(byte... x)
будет вызываться, потому что он является наиболее конкретным.
Однако
public static void main(String[] args) {
movie();
}
static void movie(int... x) { }
static void movie(boolean... x) { }
не компилируется, потому что boolean
нельзя сравнить с int
.
Ответ 2
Неформальная интуиция заключается в том, что один метод более конкретный, чем другой , если может быть передано любое обращение, обработанное первым методом на другой без ошибки времени компиляции.
Рассмотрим movie(int...x)
как M1 и movie(double...x)
как M2.
Метод M1 более специфичен, чем M2, потому что мы можем вызывать метод M2 с теми же входами, что и метод M1, без ошибок времени компиляции.
Таким образом, вызов первого метода M1 определенно обрабатывается M2. Поскольку double
может обрабатывать int
без каких-либо проблем.
Но мы не можем вызывать M1 с теми же входами, которые даны методу M2, и это легко понять.
Проверьте следующий пример,
public class Test {
public static void main(String[] args) {
movie();
}
static void movie(int... x) {
System.out.println("One argument");
}
static void movie(short... x) {
System.out.println("Short argument");
}
}
OUTPUT
Short argument
Потому что здесь short
более конкретный, чем int
для вызова метода movie()
.
С другой стороны, для метода boolean
вызов movie();
вызывает путаницу, потому что компилятор не может решить, какой метод вызывать, потому что в этом случае нет такой точки более конкретного метода.
Ответ 3
Я думаю, что причина заключается в автоматическом продвижении типа в java. По умолчанию тип выражения продвигается до типа Integer или Long (в зависимости от диапазона), если ваше выражение не содержит с плавающей запятой.
Итак, в первом случае выражение void просто разрешено для int varargs, поскольку Integer выигрывает конкурс из-за отсутствия значения с плавающей запятой в выражении.
Во втором случае компилятор не может решить, что вызывать, то есть из перегруженных методов он не может решить, какой из них вызывать без аргументов.
Ответ 4
Ответ:
1. Если мы используем объекты типов данных, такие как int как Integer, он также дает ту же ошибку. Поэтому причина в том, что он не работает с типами объектов и дает ошибки, если для функции не задан аргумент.
2. Поскольку vararg принимает аргумент как массив заданного типа. Поэтому, если вы не передаете никаких аргументов, он принимает его как массив с нулевыми аргументами и выполняет целочисленную функцию, потому что он получил одну из функций с целым аргументом.
3. Это когда вы добавляете float и double для двух разных функций и не передаете никакого аргумента, тогда он выполняет функцию аргумента float.
Решение:
- Не используйте типы объектов, такие как String, Integer, Float, Double, Boolean и Char. Вместо этого используйте int, float, double и boolean, если хотите для выполнения одной из функций без передачи аргументов.
- Итак, в данном примере нам нужно указать логический аргумент для логического объекта не-примитивного типа данных true или false.
public class File
{
public static void main(String[] args)
{
movie(true);
}
static void movie(Boolean...x) //Changed the parameter type to boolean from double
{
System.out.println("No varargs"+x);
}
static void movie(int...x)
{
System.out.println("One argument");
}
}