ошибка с varargs и перегрузкой?

Кажется, существует ошибка в 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

21
задан pstanton 26 March 2010 в 05:54
поделиться

3 ответа

это по на форумах Sun .

Никакого реального решения, только отставка.

Varargs (и автобокс, который также приводит к трудному отслеживанию поведения, особенно в сочетании с varargs) были закреплены позже в жизни Java, и это одна из областей, где это проявляется. Так что это скорее ошибка в спецификации, чем в компиляторе.

По крайней мере, это дает хорошие (?) Вопросы о трюках SCJP.

7
ответ дан 29 November 2019 в 21:44
поделиться

Проблема в том, что является неоднозначным.

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).

13
ответ дан 29 November 2019 в 21:44
поделиться

Интересно. К счастью, есть несколько способов избежать этой проблемы:

Вы можете использовать типы оболочки вместо подписей методов:

   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);
      }
   }
4
ответ дан 29 November 2019 в 21:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: