Кажется, существует ошибка в Java varargs реализация. Java не может отличить соответствующий тип, когда метод перегружается с различными типами vararg параметров.
Это дает мне ошибку The method ... is ambiguous for the type ...
Рассмотрите следующий код:
public class Test
{
public static void main(String[] args) throws Throwable
{
doit(new int[]{1, 2}); // <- no problem
doit(new double[]{1.2, 2.2}); // <- no problem
doit(1.2f, 2.2f); // <- no problem
doit(1.2d, 2.2d); // <- no problem
doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test
}
public static void doit(double... ds)
{
System.out.println("doubles");
}
public static void doit(int... is)
{
System.out.println("ints");
}
}
в документах говорится: "Вообще говоря, Вы не должны перегружать varargs метод, или для программистов будет трудно выяснить, какую перегрузку называют".
однако они не упоминают эту ошибку, и это не программисты, которые находят это трудным, это - компилятор.
мысли?
РЕДАКТИРОВАНИЕ - Компилятор: Sun jdk 1.6.0 u18
это по на форумах Sun .
Никакого реального решения, только отставка.
Varargs (и автобокс, который также приводит к трудному отслеживанию поведения, особенно в сочетании с varargs) были закреплены позже в жизни Java, и это одна из областей, где это проявляется. Так что это скорее ошибка в спецификации, чем в компиляторе.
По крайней мере, это дает хорошие (?) Вопросы о трюках SCJP.
Проблема в том, что является неоднозначным.
doIt(1, 2);
может быть вызовом doIt (int ...)
или doIt (double ...)
. В последнем случае целочисленные литералы будут преобразованы в значения double
.
Я почти уверен, что в спецификации Java сказано, что это неоднозначная конструкция, а компилятор просто следует правилам, установленным в спецификации. (Чтобы убедиться в этом, мне придется изучить это дополнительно.)
РЕДАКТИРОВАТЬ - соответствующая часть JLS - это « 15.12.2.5 Выбор наиболее конкретного метода », но он делает у меня болит голова.
Я думаю, что причина заключалась бы в том, что void doIt (int [])
не является более конкретным (или наоборот), чем void doIt (double [])
, потому что ] int []
не является подтипом double []
(и наоборот). Поскольку две перегрузки одинаково специфичны, вызов неоднозначен.
Напротив, void doItAgain (int)
более специфичен, чем void doItAgain (double)
, потому что int
является подтипом двойной
согласно JLS.Следовательно, вызов doItAgain (42)
не является двусмысленным.
EDIT 2 - @finnw правильно, это ошибка. Рассмотрим эту часть 15.12.2.5 (отредактировано для удаления неприменимых случаев):
Один метод члена переменной арности с именем m более конкретен , чем другой метод члена переменной арности с тем же именем, если:
Один метод-член имеет n параметров, а другой - k параметров, где n ≥ k. Типы параметров первого метода-члена - T1,. . . , Tn-1, Tn [], типы параметров другого метода - U1,. . . , Ук-1, Ук []. Пусть Si = Ui, 1 <= i <= k. Затем:
- для всех j от 1 до k-1, Tj <: Sj и,
- для всех j от k до n, Tj <: Sk
Примените это к случаю, когда n = k = 1, и мы видим, что doIt (int [])
более специфичен, чем doIt (double [])
.
На самом деле, есть отчет об ошибке для этого , и Sun признает, что это действительно ошибка, хотя они отдали ей приоритет как «очень низкий» . Ошибка теперь помечена как исправленная в Java 7 (b123).
Интересно. К счастью, есть несколько способов избежать этой проблемы:
Вы можете использовать типы оболочки вместо подписей методов:
public static void doit(Double... ds) {
for(Double currD : ds) {
System.out.println(currD);
}
}
public static void doit(Integer... is) {
for(Integer currI : is) {
System.out.println(currI);
}
}
Или вы можете использовать обобщения:
public static <T> void doit(T... ts) {
for(T currT : ts) {
System.out.println(currT);
}
}