Подтвердить что ты не робот

Почему autoboxing не отменяет varargs при использовании перегрузки методов в Java 7?

У нас есть класс LogManager в нашем Java-проекте, который выглядит так:

public class LogManager {

    public void log(Level logLevel, Object... args) {
        // do something
    }

    public void log(Level logLevel, int value, Object... args) {
        // do something else
    }
}

При компиляции проекта с OpenJDK 6 под Debian everyting работает отлично. При использовании OpenJDK 7 сборка (выполняется с помощью ant) выдает следующие ошибки и сбой сборки:

[javac] /…/LogManager.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       log(logLevel, 1, logMessage);
[javac]       ^
[javac] /…/SomeOtherClass.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       logger.log(logLevel, 1, logMessage);
[javac]             ^

Пока 1 не является автобоксированным, вызов метода должен быть однозначный, поскольку 1 является int и не может быть преобразован в Object. Так почему не autoboxing отменяет varargs здесь?

Eclipse (установленный с использованием tar.gz с eclipse.org) не компилирует его если OpenJDK 6 установлен или нет.

Большое спасибо за вашу помощь!

Edit:

В обоих случаях компилятор получает опцию source="1.6" и target="1.6". Замечание компиляции Eclipse означает только комментарий.

4b9b3361

Ответ 1

Я предполагаю, что это связано с ошибкой # 6886431, которая, похоже, исправлена ​​и в OpenJDK 7.

Проблема заключается в том, что JLS 15.12.2.5 Выбор наиболее конкретного метода  говорит, что один метод более конкретный, чем другой, когда типы формальных параметров первого являются подтипами формальных параметров последнего.

Так как int не является подтипом Object, ни один из ваших методов не является наиболее конкретным, поэтому ваш вызов является неоднозначным.

Однако возможно следующее обходное решение, поскольку Integer является подтипом Object:

public void log(Level logLevel, Object... args) { ... }
public void log(Level logLevel, Integer value, Object... args) { ... } 

Ответ 2

Eclipse использует свой собственный компилятор, поэтому то, что Eclipse в конечном итоге следует тому, что делают компиляторы SUN/Oracle; однако иногда (как в этом случае) существуют различия.

Это может "идти в одну сторону" и, вероятно, в Java 6, проблема не была подробно рассмотрена. Поскольку у Java есть сильное требование уменьшить количество "неоднозначных" значений в своей среде (чтобы обеспечить одинаковое поведение на многих платформах), я бы предположил, что они подтянули (или прямо указали) решительное поведение в версии 7.

Вы просто попались на "неправильную" сторону нового уточнения спецификации. Извините, но я думаю, вы будете писать немного этого

public void log(Level logLevel, Object... args) {
    if (args != null && args[0] instanceof Integer) {
      // do something else
    } else {
      // do something
    }
}

в ваше новое решение.

Ответ 3

Это приближается ближе к краю, чем разумно. Если вы не найдете четкого языка в спецификации поведения, я бы избегал чего-то двусмысленного.

Даже если это указано в спецификации, читатели вашего кода не будут знать юридическое знание языка, поэтому вам нужно будет прокомментировать его, чтобы объяснить, и они могут или не могут прочитать комментарий. Возможно, они даже не рассматривают одну из альтернатив - просто посмотрите одну перегрузку, которая подходит, и бегите с этим. Авария в ожидании.