ошибка с varargs и перегрузкой? - PullRequest
21 голосов
/ 26 марта 2010

Кажется, есть ошибка в реализации 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

Ответы [ 3 ]

13 голосов
/ 26 марта 2010

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

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 является подтипом double в соответствии с JLS. Следовательно, вызов doItAgain(42) не является двусмысленным.

РЕДАКТИРОВАТЬ 2 - @finnw прав, это ошибка. Рассмотрим эту часть 15.12.2.5 (отредактирован для удаления неприменимых случаев):

Один переменный метод члена арности с именем m является более конкретным , чем другой метод переменного члена арности с тем же именем, если:

Один метод-член имеет n параметров, а другой - k параметров, где n ≥ k. Типы параметров первого метода-члена: T1,. , , , Tn-1, Tn [], типами параметров другого метода являются U1,. , , , Великобритания-1, Великобритания. Пусть Si = Ui, 1 <= i <= k. Тогда: </p>

  • для всех j от 1 до k-1, Tj <: Sj и </li>
  • для всех j от k до n, Tj <: Sk </li>

Примените это к случаю, когда n = k = 1, и мы увидим, что doIt(int[]) более конкретно, чем doIt(double[]).


На самом деле, существует отчет об ошибке для этого , и Sun признает, что это действительно ошибка, хотя они расценили ее как "очень низкую" . Ошибка помечена как исправленная в Java 7 (b123).

7 голосов
/ 26 марта 2010

Об этом обсуждается более на форумах Sun .

Там нет реального разрешения, просто отставка.

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

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

4 голосов
/ 26 марта 2010

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

Вместо этого вы можете использовать типы оболочки в сигнатурах метода:

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