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

Статический импорт Java

Просто экспериментом я обнаружил, что Java-статические методы переопределяют все одинаковые именованные методы в области видимости даже в статическом контексте. Даже без разрешения перегрузки параметров. Как

import java.util.Arrays;    
import static java.util.Arrays.toString;

public class A {
    public static void bar(Object... args) {
        Arrays.toString(args);
        toString(args);     //toString() in java.lang.Object cannot be applied to (java.lang.Object[])
    }
}

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

UPD: Java 6 не компилирует этот пример. Вопрос в том, почему?

4b9b3361

Ответ 1

Объяснение простое, хотя оно не меняет того факта, что поведение крайне неинтуитивно:

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

Теперь, что здесь происходит, заключается в том, что наименьшая охватывающая область, содержащая метод toString(), является классом A, который наследует ее от Object. Следовательно, мы останавливаемся и не идем дальше. К сожалению, следующий компилятор пытается найти наилучшее соответствие методов в данной области и замечает, что он не может назвать никого из них и дает ошибку.

Это означает, что никогда статически импортировать методы с именем, которое идентично методу в объекте, потому что методы, естественно имеющие область видимости, имеют приоритет перед статическим импортом (JLS описывает метод затенения в деталях, но для этой проблемы я думаю, что гораздо проще просто запомнить это).

Изменить: @alf любезно предоставил правую часть JLS, что описывает вызов метода для тех, кто хочет всю картину. Это довольно сложно, но тогда проблема не просто так, как можно было бы ожидать.

Ответ 2

Это не переопределение. Если это сработало, this.toString() все равно будет обращаться к методу A вместо Arrays.toString, как это было бы, если бы произошло переопределение.

Спецификация языка объясняет, что статический импорт влияет только на разрешение методов и типов static:

Одностатическая декларация импорта d в ​​блоке компиляции c пакета p, который импортирует поле с именем n, тенью объявляет любое статическое поле с именем n, импортированное декларацией static-import-on-demand в c, в течение c.

Одностатическая декларация импорта d в ​​компиляционной единице c пакета p, которая импортирует метод с именем n с тенью подписи s объявляет любой статический метод с именем n с сигнатурой, импортированной с помощью static-import-on-demand в c, на протяжении всего c.

Одностатическая декларация импорта d в ​​блоке компиляции c пакета p, который импортирует тип с именем n, тени объявлениями:

  • любой статический тип с именем n, импортированный декларацией static-import-on-demand в c.
  • любой тип верхнего уровня (§7.6), названный n, объявленный в другой единице компиляции (§7.3) p.
  • любой тип с именем n, импортированный по типу импорта по запросу (§7.5.2) в c. на протяжении c.

Статический импорт не затеняет нестатические методы или внутренние типы.

Таким образом, toString не затеняет нестатический метод. Поскольку имя toString может ссылаться на нестатический метод A, он не может ссылаться на метод static Arrays и, следовательно, toString связывается с единственным методом с именем toString, который доступен в области видимости String toString(). Этот метод не может принимать никаких аргументов, поэтому вы получаете ошибку компиляции.

Раздел 15.12.1 объясняет разрешение метода и должен был быть полностью переписан, чтобы можно было скрывать недоступные имена методов в методах static, но не внутри member методов.

Я предполагаю, что разработчики языка хотели, чтобы правила разрешения методов были простыми, а это означает, что одно и то же имя означает то же самое, независимо от того, появляется ли оно в методе static или нет, и единственное, что меняется, которое доступно.

Ответ 3

Если вы попытаетесь выполнить аналогичный код, то вы не будете получать ошибку компилятора

import static java.util.Arrays.sort;
public class StaticImport {
    public void bar(int... args) {
        sort(args); // will call Array.sort
    }
}

Причина, по которой это компилируется, а ваша - нет, заключается в том, что toString() (или любой другой метод, определенный в объекте класса) по-прежнему привязаны к классу Object из-за того, что Object является родителем вашего класса. Следовательно, когда компилятор находит соответствующую подпись этих методов из класса Object, он дает ошибку компилятора. В моем примере, поскольку класс Object не имеет метода sort(int[]), следовательно, компилятор правильно сопоставляет его со статическим импортом.

Ответ 4

Я не думаю, что это ошибка или что-то отличное от обычного импорта. Например, в случае нормального импорта, если у вас есть закрытый класс с тем же именем, что и импортированный, импортированный не будет отображаться.