Почему автобокс не отменяет varargs при использовании перегрузки методов в Java 7? - PullRequest
19 голосов
/ 07 октября 2011

В нашем Java-проекте есть класс LogManager, который выглядит следующим образом:

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 является целым числом и не может быть преобразовано в Object. Так почему не автобокс отменяет здесь varargs?

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

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

Edit:

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

Ответы [ 3 ]

17 голосов
/ 07 октября 2011

Я думаю, это связано с ошибкой # 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 голосов
/ 07 октября 2011

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
    }
}

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

0 голосов
/ 07 октября 2011

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

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

...